diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 89dcac3f41..c0c52d2ce6 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -15,6 +15,7 @@ const ir = @import("ir.zig"); const zir = @import("zir.zig"); const Module = @This(); const Inst = ir.Inst; +const Body = ir.Body; const ast = std.zig.ast; const trace = @import("tracy.zig").trace; @@ -649,11 +650,19 @@ pub const Scope = struct { pub const Block = struct { pub const base_tag: Tag = .block; base: Scope = Scope{ .tag = base_tag }, + parent: ?*Block, func: ?*Fn, decl: *Decl, instructions: ArrayListUnmanaged(*Inst), /// Points to the arena allocator of DeclAnalysis arena: *Allocator, + label: ?Label = null, + + pub const Label = struct { + name: []const u8, + results: ArrayListUnmanaged(*Inst), + block_inst: *Inst.Block, + }; }; /// This is a temporary structure, references to it are valid only @@ -676,10 +685,6 @@ pub const Scope = struct { }; }; -pub const Body = struct { - instructions: []*Inst, -}; - pub const AllErrors = struct { arena: std.heap.ArenaAllocator.State, list: []const Message, @@ -1139,13 +1144,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const body_node = fn_proto.body_node orelse return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{}); - if (fn_proto.params_len != 0) { - return self.failTok( - &fn_type_scope.base, - fn_proto.params()[0].name_token.?, - "TODO implement function parameters", - .{}, - ); + + const param_decls = fn_proto.params(); + const param_types = try fn_type_scope.arena.allocator.alloc(*zir.Inst, param_decls.len); + for (param_decls) |param_decl, i| { + const param_type_node = switch (param_decl.param_type) { + .var_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), + .var_args => |tok| return self.failTok(&fn_type_scope.base, tok, "TODO implement var args", .{}), + .type_expr => |node| node, + }; + param_types[i] = try self.astGenExpr(&fn_type_scope.base, param_type_node); } if (fn_proto.lib_name) |lib_name| { return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{}); @@ -1174,7 +1182,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const fn_src = tree.token_locs[fn_proto.fn_token].start; const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{ .return_type = return_type_inst, - .param_types = &[0]*zir.Inst{}, + .param_types = param_types, }, .{}); _ = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.Return, .{ .operand = fn_type_inst }, .{}); @@ -1184,6 +1192,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); var block_scope: Scope.Block = .{ + .parent = null, .func = null, .decl = decl, .instructions = .{}, @@ -1302,10 +1311,25 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir .Call => return self.astGenCall(scope, @fieldParentPtr(ast.Node.Call, "base", ast_node)), .Unreachable => return self.astGenUnreachable(scope, @fieldParentPtr(ast.Node.Unreachable, "base", ast_node)), .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)), + .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)), else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}), } } +fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst { + if (if_node.payload) |payload| { + return self.failNode(scope, payload, "TODO implement astGenIf for optionals", .{}); + } + if (if_node.@"else") |else_node| { + if (else_node.payload) |payload| { + return self.failNode(scope, payload, "TODO implement astGenIf for error unions", .{}); + } + } + const cond = try self.astGenExpr(scope, if_node.condition); + const body = try self.astGenExpr(scope, if_node.condition); + return self.failNode(scope, if_node.condition, "TODO implement astGenIf", .{}); +} + fn astGenControlFlowExpression( self: *Module, scope: *Scope, @@ -1351,7 +1375,18 @@ fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerE ), error.InvalidCharacter => break :integer, }; - return self.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}); + const val = switch (bit_count) { + 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type), + 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type), + 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type), + 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type), + else => return self.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}), + }; + const src = tree.token_locs[ident.token].start; + return self.addZIRInstConst(scope, src, .{ + .ty = Type.initTag(.type), + .val = val, + }); } } @@ -1494,16 +1529,18 @@ fn astGenBuiltinCall(self: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) fn astGenCall(self: *Module, scope: *Scope, call: *ast.Node.Call) InnerError!*zir.Inst { const tree = scope.tree(); - - if (call.params_len != 0) { - return self.failNode(scope, &call.base, "TODO implement fn calls with parameters", .{}); - } const lhs = try self.astGenExpr(scope, call.lhs); + const param_nodes = call.params(); + const args = try scope.cast(Scope.GenZIR).?.arena.allocator.alloc(*zir.Inst, param_nodes.len); + for (param_nodes) |param_node, i| { + args[i] = try self.astGenExpr(scope, param_node); + } + const src = tree.token_locs[call.lhs.firstToken()].start; return self.addZIRInst(scope, src, zir.Inst.Call, .{ .func = lhs, - .args = &[0]*zir.Inst{}, + .args = args, }, .{}); } @@ -1871,6 +1908,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator); defer decl.typed_value.most_recent.arena.?.* = arena.state; var inner_block: Scope.Block = .{ + .parent = null, .func = func, .decl = decl, .instructions = .{}, @@ -2323,7 +2361,10 @@ fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) I fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { switch (old_inst.tag) { + .arg => return self.analyzeInstArg(scope, old_inst.cast(zir.Inst.Arg).?), + .block => return self.analyzeInstBlock(scope, old_inst.cast(zir.Inst.Block).?), .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?), + .breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.cast(zir.Inst.BreakVoid).?), .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?), .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?), .@"const" => return self.analyzeInstConst(scope, old_inst.cast(zir.Inst.Const).?), @@ -2436,11 +2477,105 @@ fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.Compile return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); } +fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { + const b = try self.requireRuntimeBlock(scope, inst.base.src); + const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; + const param_count = fn_ty.fnParamLen(); + if (inst.positionals.index >= param_count) { + return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{ + inst.positionals.index, + param_count, + }); + } + const param_type = fn_ty.fnParamType(inst.positionals.index); + return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, .{ + .index = inst.positionals.index, + }); +} + +fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { + const parent_block = scope.cast(Scope.Block).?; + + // Reserve space for a Block instruction so that generated Break instructions can + // point to it, even if it doesn't end up getting used because the code ends up being + // comptime evaluated. + const block_inst = try parent_block.arena.create(Inst.Block); + block_inst.* = .{ + .base = .{ + .tag = Inst.Block.base_tag, + .ty = undefined, // Set after analysis. + .src = inst.base.src, + }, + .args = undefined, + }; + + var child_block: Scope.Block = .{ + .parent = parent_block, + .func = parent_block.func, + .decl = parent_block.decl, + .instructions = .{}, + .arena = parent_block.arena, + // TODO @as here is working around a miscompilation compiler bug :( + .label = @as(?Scope.Block.Label, Scope.Block.Label{ + .name = inst.positionals.label, + .results = .{}, + .block_inst = block_inst, + }), + }; + const label = &child_block.label.?; + + defer child_block.instructions.deinit(self.allocator); + defer label.results.deinit(self.allocator); + + try self.analyzeBody(&child_block.base, inst.positionals.body); + + // Blocks must terminate with noreturn instruction. + assert(child_block.instructions.items.len != 0); + assert(child_block.instructions.items[child_block.instructions.items.len - 1].tag.isNoReturn()); + + if (label.results.items.len <= 1) { + // No need to add the Block instruction; we can add the instructions to the parent block directly. + // Blocks are terminated with a noreturn instruction which we do not want to include. + const instrs = child_block.instructions.items; + try parent_block.instructions.appendSlice(self.allocator, instrs[0 .. instrs.len - 1]); + if (label.results.items.len == 1) { + return label.results.items[0]; + } else { + return self.constNoReturn(scope, inst.base.src); + } + } + + // Need to set the type and emit the Block instruction. This allows machine code generation + // to emit a jump instruction to after the block when it encounters the break. + try parent_block.instructions.append(self.allocator, &block_inst.base); + block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items); + block_inst.args.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; + return &block_inst.base; +} + fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { const b = try self.requireRuntimeBlock(scope, inst.base.src); return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {}); } +fn analyzeInstBreakVoid(self: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { + const label_name = inst.positionals.label; + const void_inst = try self.constVoid(scope, inst.base.src); + + var opt_block = scope.cast(Scope.Block); + while (opt_block) |block| { + if (block.label) |*label| { + if (mem.eql(u8, label.name, label_name)) { + try label.results.append(self.allocator, void_inst); + return self.constNoReturn(scope, inst.base.src); + } + } + opt_block = block.parent; + } else { + return self.fail(scope, inst.base.src, "use of undeclared label '{}'", .{label_name}); + } +} + fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { const decl_name = try self.resolveConstString(scope, inst.positionals.name); return self.analyzeDeclRefByName(scope, inst.base.src, decl_name); @@ -2602,35 +2737,38 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { const return_type = try self.resolveType(scope, fntype.positionals.return_type); - if (return_type.zigTypeTag() == .NoReturn and - fntype.positionals.param_types.len == 0 and - fntype.kw_args.cc == .Unspecified) - { - return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); + // Hot path for some common function types. + if (fntype.positionals.param_types.len == 0) { + if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) { + return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); + } + + if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) { + return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); + } + + if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) { + return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); + } + + if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) { + return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); + } } - if (return_type.zigTypeTag() == .Void and - fntype.positionals.param_types.len == 0 and - fntype.kw_args.cc == .Unspecified) - { - return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); + const arena = scope.arena(); + const param_types = try arena.alloc(Type, fntype.positionals.param_types.len); + for (fntype.positionals.param_types) |param_type, i| { + param_types[i] = try self.resolveType(scope, param_type); } - if (return_type.zigTypeTag() == .NoReturn and - fntype.positionals.param_types.len == 0 and - fntype.kw_args.cc == .Naked) - { - return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); - } - - if (return_type.zigTypeTag() == .Void and - fntype.positionals.param_types.len == 0 and - fntype.kw_args.cc == .C) - { - return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); - } - - return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{}); + const payload = try arena.create(Type.Payload.Function); + payload.* = .{ + .cc = fntype.kw_args.cc, + .return_type = return_type, + .param_types = param_types, + }; + return self.constType(scope, fntype.base.src, Type.initPayload(&payload.base)); } fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { @@ -2757,10 +2895,17 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn } fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const lhs = try self.resolveInst(scope, inst.positionals.lhs); const rhs = try self.resolveInst(scope, inst.positionals.rhs); if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) { + if (!lhs.ty.eql(rhs.ty)) { + return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{}); + } + if (lhs.value()) |lhs_val| { if (rhs.value()) |rhs_val| { // TODO is this a performance issue? maybe we should try the operation without @@ -2777,10 +2922,6 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError! result_bigint.add(lhs_bigint, rhs_bigint); const result_limbs = result_bigint.limbs[0..result_bigint.len]; - if (!lhs.ty.eql(rhs.ty)) { - return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{}); - } - const val_payload = if (result_bigint.positive) blk: { const val_payload = try scope.arena().create(Value.Payload.IntBigPositive); val_payload.* = .{ .limbs = result_limbs }; @@ -2797,6 +2938,12 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError! }); } } + + const b = try self.requireRuntimeBlock(scope, inst.base.src); + return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{ + .lhs = lhs, + .rhs = rhs, + }); } return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{}); @@ -2936,6 +3083,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner const parent_block = try self.requireRuntimeBlock(scope, inst.base.src); var true_block: Scope.Block = .{ + .parent = parent_block, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, @@ -2945,6 +3093,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner try self.analyzeBody(&true_block.base, inst.positionals.true_body); var false_block: Scope.Block = .{ + .parent = parent_block, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, @@ -3178,7 +3327,7 @@ fn cmpNumeric( const casted_lhs = try self.coerce(scope, dest_type, lhs); const casted_rhs = try self.coerce(scope, dest_type, lhs); - return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{ + return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{ .lhs = casted_lhs, .rhs = casted_rhs, .op = op, @@ -3197,6 +3346,12 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { } } +fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { + if (instructions.len == 0) + return Type.initTag(.noreturn); + return self.fail(scope, instructions[0].src, "TODO peer type resolution", .{}); +} + fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { // If the types are the same, we can return the operand. if (dest_type.eql(inst.ty)) @@ -3238,7 +3393,10 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { if (inst.value()) |val| { return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); } else { - return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{}); + return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{ + inst.ty, + dest_type, + }); } } else { return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type }); diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 7473ccc431..686edda373 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -174,6 +174,9 @@ const Function = struct { fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { switch (inst.tag) { + .add => return self.genAdd(inst.cast(ir.Inst.Add).?), + .arg => return self.genArg(inst.src), + .block => return self.genBlock(inst.cast(ir.Inst.Block).?), .breakpoint => return self.genBreakpoint(inst.src), .call => return self.genCall(inst.cast(ir.Inst.Call).?), .unreach => return MCValue{ .unreach = {} }, @@ -190,6 +193,19 @@ const Function = struct { } } + fn genAdd(self: *Function, inst: *ir.Inst.Add) !MCValue { + switch (self.target.cpu.arch) { + else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}), + } + } + + fn genArg(self: *Function, src: usize) !MCValue { + switch (self.target.cpu.arch) { + else => return self.fail(src, "TODO implement function parameters for {}", .{self.target.cpu.arch}), + } + return .none; + } + fn genBreakpoint(self: *Function, src: usize) !MCValue { switch (self.target.cpu.arch) { .i386, .x86_64 => { @@ -302,6 +318,12 @@ const Function = struct { } } + fn genBlock(self: *Function, inst: *ir.Inst.Block) !MCValue { + switch (self.target.cpu.arch) { + else => return self.fail(inst.base.src, "TODO implement codegen Block for {}", .{self.target.cpu.arch}), + } + } + fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue { // TODO convert to inline function switch (self.target.cpu.arch) { diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 387c88df3b..cb9c7b5e11 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -15,8 +15,11 @@ pub const Inst = struct { src: usize, pub const Tag = enum { + add, + arg, assembly, bitcast, + block, breakpoint, call, cmp, @@ -28,6 +31,33 @@ pub const Inst = struct { ret, retvoid, unreach, + + /// Returns whether the instruction is one of the control flow "noreturn" types. + /// Function calls do not count. When ZIR is generated, the compiler automatically + /// emits an `Unreach` after a function call with the `noreturn` return type. + pub fn isNoReturn(tag: Tag) bool { + return switch (tag) { + .add, + .arg, + .assembly, + .bitcast, + .block, + .breakpoint, + .cmp, + .constant, + .isnonnull, + .isnull, + .ptrtoint, + .call, + => false, + + .condbr, + .ret, + .retvoid, + .unreach, + => true, + }; + } }; pub fn cast(base: *Inst, comptime T: type) ?*T { @@ -50,6 +80,25 @@ pub const Inst = struct { return inst.val; } + pub const Add = struct { + pub const base_tag = Tag.add; + base: Inst, + + args: struct { + lhs: *Inst, + rhs: *Inst, + }, + }; + + pub const Arg = struct { + pub const base_tag = Tag.arg; + base: Inst, + + args: struct { + index: usize, + }, + }; + pub const Assembly = struct { pub const base_tag = Tag.assembly; base: Inst, @@ -73,6 +122,14 @@ pub const Inst = struct { }, }; + pub const Block = struct { + pub const base_tag = Tag.block; + base: Inst, + args: struct { + body: Body, + }, + }; + pub const Breakpoint = struct { pub const base_tag = Tag.breakpoint; base: Inst, @@ -105,8 +162,8 @@ pub const Inst = struct { base: Inst, args: struct { condition: *Inst, - true_body: Module.Body, - false_body: Module.Body, + true_body: Body, + false_body: Body, }, }; @@ -164,3 +221,7 @@ pub const Inst = struct { args: void, }; }; + +pub const Body = struct { + instructions: []*Inst, +}; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index fb97186648..2a02d0d89b 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -21,8 +21,14 @@ pub const Type = extern union { switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -57,6 +63,7 @@ pub const Type = extern union { .fn_void_no_args => return .Fn, .fn_naked_noreturn_no_args => return .Fn, .fn_ccc_void_no_args => return .Fn, + .function => return .Fn, .array, .array_u8_sentinel_0 => return .Array, .single_const_pointer => return .Pointer, @@ -126,10 +133,14 @@ pub const Type = extern union { @panic("TODO implement more pointer Type equality comparison"); }, .Int => { - if (a.tag() != b.tag()) { - // Detect that e.g. u64 != usize, even if the bits match on a particular target. + // Detect that e.g. u64 != usize, even if the bits match on a particular target. + const a_is_named_int = a.isNamedInt(); + const b_is_named_int = b.isNamedInt(); + if (a_is_named_int != b_is_named_int) return false; - } + if (a_is_named_int) + return a.tag() == b.tag(); + // Remaining cases are arbitrary sized integers. // The target will not be branched upon, because we handled target-dependent cases above. const info_a = a.intInfo(@as(Target, undefined)); const info_b = b.intInfo(@as(Target, undefined)); @@ -176,8 +187,14 @@ pub const Type = extern union { } else switch (self.ptr_otherwise.tag) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -231,6 +248,21 @@ pub const Type = extern union { }, .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned), .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned), + .function => { + const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise); + const new_payload = try allocator.create(Payload.Function); + const param_types = try allocator.alloc(Type, payload.param_types.len); + for (payload.param_types) |param_type, i| { + param_types[i] = try param_type.copy(allocator); + } + new_payload.* = .{ + .base = payload.base, + .return_type = try payload.return_type.copy(allocator), + .param_types = param_types, + .cc = payload.cc, + }; + return Type{ .ptr_otherwise = &new_payload.base }; + }, } } @@ -246,7 +278,7 @@ pub const Type = extern union { comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var, - ) !void { + ) @TypeOf(out_stream).Error!void { comptime assert(fmt.len == 0); var ty = self; while (true) { @@ -254,8 +286,14 @@ pub const Type = extern union { switch (t) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -288,6 +326,16 @@ pub const Type = extern union { .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), + .function => { + const payload = @fieldParentPtr(Payload.Function, "base", ty.ptr_otherwise); + try out_stream.writeAll("fn("); + for (payload.param_types) |param_type, i| { + if (i != 0) try out_stream.writeAll(", "); + try param_type.format("", .{}, out_stream); + } + try out_stream.writeAll(") "); + try payload.return_type.format("", .{}, out_stream); + }, .array_u8_sentinel_0 => { const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise); @@ -322,8 +370,14 @@ pub const Type = extern union { switch (self.tag()) { .u8 => return Value.initTag(.u8_type), .i8 => return Value.initTag(.i8_type), - .isize => return Value.initTag(.isize_type), + .u16 => return Value.initTag(.u16_type), + .i16 => return Value.initTag(.i16_type), + .u32 => return Value.initTag(.u32_type), + .i32 => return Value.initTag(.i32_type), + .u64 => return Value.initTag(.u64_type), + .i64 => return Value.initTag(.i64_type), .usize => return Value.initTag(.usize_type), + .isize => return Value.initTag(.isize_type), .c_short => return Value.initTag(.c_short_type), .c_ushort => return Value.initTag(.c_ushort_type), .c_int => return Value.initTag(.c_int_type), @@ -365,8 +419,14 @@ pub const Type = extern union { return switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -386,6 +446,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .single_const_pointer_to_comptime_int, .const_slice_u8, .array_u8_sentinel_0, @@ -417,9 +478,14 @@ pub const Type = extern union { .fn_void_no_args, // represents machine code; not a pointer .fn_naked_noreturn_no_args, // represents machine code; not a pointer .fn_ccc_void_no_args, // represents machine code; not a pointer + .function, // represents machine code; not a pointer .array_u8_sentinel_0, => return 1, + .i16, .u16 => return 2, + .i32, .u32 => return 4, + .i64, .u64 => return 8, + .isize, .usize, .single_const_pointer_to_comptime_int, @@ -473,8 +539,14 @@ pub const Type = extern union { return switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -505,6 +577,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .int_unsigned, .int_signed, => false, @@ -519,8 +592,14 @@ pub const Type = extern union { return switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -552,6 +631,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .int_unsigned, .int_signed, => false, @@ -565,8 +645,14 @@ pub const Type = extern union { return switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -596,6 +682,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .int_unsigned, .int_signed, => unreachable, @@ -612,8 +699,14 @@ pub const Type = extern union { return switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -641,6 +734,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .int_unsigned, .int_signed, => unreachable, @@ -657,8 +751,14 @@ pub const Type = extern union { return switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -686,6 +786,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .single_const_pointer, .single_const_pointer_to_comptime_int, .const_slice_u8, @@ -703,8 +804,14 @@ pub const Type = extern union { return switch (self.tag()) { .u8, .i8, - .isize, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, + .isize, .c_short, .c_ushort, .c_int, @@ -732,6 +839,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .single_const_pointer, .single_const_pointer_to_comptime_int, .const_slice_u8, @@ -766,6 +874,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -778,6 +887,9 @@ pub const Type = extern union { .c_uint, .c_ulong, .c_ulonglong, + .u16, + .u32, + .u64, => false, .int_signed, @@ -787,11 +899,14 @@ pub const Type = extern union { .c_int, .c_long, .c_longlong, + .i16, + .i32, + .i64, => true, }; } - /// Asserts the type is a fixed-width integer. + /// Asserts the type is an integer. pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } { return switch (self.tag()) { .f16, @@ -813,6 +928,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -824,6 +940,12 @@ pub const Type = extern union { .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits }, .u8 => .{ .signed = false, .bits = 8 }, .i8 => .{ .signed = true, .bits = 8 }, + .u16 => .{ .signed = false, .bits = 16 }, + .i16 => .{ .signed = true, .bits = 16 }, + .u32 => .{ .signed = false, .bits = 32 }, + .i32 => .{ .signed = true, .bits = 32 }, + .u64 => .{ .signed = false, .bits = 64 }, + .i64 => .{ .signed = true, .bits = 64 }, .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() }, .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() }, .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) }, @@ -837,6 +959,59 @@ pub const Type = extern union { }; } + pub fn isNamedInt(self: Type) bool { + return switch (self.tag()) { + .f16, + .f32, + .f64, + .f128, + .c_longdouble, + .c_void, + .bool, + .void, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .noreturn, + .@"null", + .@"undefined", + .fn_noreturn_no_args, + .fn_void_no_args, + .fn_naked_noreturn_no_args, + .fn_ccc_void_no_args, + .function, + .array, + .single_const_pointer, + .single_const_pointer_to_comptime_int, + .array_u8_sentinel_0, + .const_slice_u8, + .int_unsigned, + .int_signed, + .u8, + .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, + => false, + + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + => true, + }; + } + pub fn isFloat(self: Type) bool { return switch (self.tag()) { .f16, @@ -870,6 +1045,7 @@ pub const Type = extern union { .fn_void_no_args => 0, .fn_naked_noreturn_no_args => 0, .fn_ccc_void_no_args => 0, + .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).param_types.len, .f16, .f32, @@ -893,6 +1069,12 @@ pub const Type = extern union { .const_slice_u8, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -917,6 +1099,10 @@ pub const Type = extern union { .fn_void_no_args => return, .fn_naked_noreturn_no_args => return, .fn_ccc_void_no_args => return, + .function => { + const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise); + std.mem.copy(Type, types, payload.param_types); + }, .f16, .f32, @@ -940,6 +1126,68 @@ pub const Type = extern union { .const_slice_u8, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .int_unsigned, + .int_signed, + => unreachable, + } + } + + /// Asserts the type is a function. + pub fn fnParamType(self: Type, index: usize) Type { + switch (self.tag()) { + .function => { + const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise); + return payload.param_types[index]; + }, + + .fn_noreturn_no_args, + .fn_void_no_args, + .fn_naked_noreturn_no_args, + .fn_ccc_void_no_args, + .f16, + .f32, + .f64, + .f128, + .c_longdouble, + .c_void, + .bool, + .void, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .noreturn, + .@"null", + .@"undefined", + .array, + .single_const_pointer, + .single_const_pointer_to_comptime_int, + .array_u8_sentinel_0, + .const_slice_u8, + .u8, + .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -966,6 +1214,8 @@ pub const Type = extern union { .fn_ccc_void_no_args, => Type.initTag(.void), + .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).return_type, + .f16, .f32, .f64, @@ -988,6 +1238,12 @@ pub const Type = extern union { .const_slice_u8, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -1011,6 +1267,7 @@ pub const Type = extern union { .fn_void_no_args => .Unspecified, .fn_naked_noreturn_no_args => .Naked, .fn_ccc_void_no_args => .C, + .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).cc, .f16, .f32, @@ -1034,6 +1291,12 @@ pub const Type = extern union { .const_slice_u8, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -1057,6 +1320,7 @@ pub const Type = extern union { .fn_void_no_args => false, .fn_naked_noreturn_no_args => false, .fn_ccc_void_no_args => false, + .function => false, .f16, .f32, @@ -1080,6 +1344,12 @@ pub const Type = extern union { .const_slice_u8, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -1107,6 +1377,12 @@ pub const Type = extern union { .comptime_float, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -1133,6 +1409,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -1154,6 +1431,12 @@ pub const Type = extern union { .comptime_float, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -1171,6 +1454,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .single_const_pointer_to_comptime_int, .array_u8_sentinel_0, .const_slice_u8, @@ -1211,6 +1495,12 @@ pub const Type = extern union { .comptime_float, .u8, .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, .usize, .isize, .c_short, @@ -1228,6 +1518,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .function, .single_const_pointer_to_comptime_int, .array_u8_sentinel_0, .const_slice_u8, @@ -1254,8 +1545,14 @@ pub const Type = extern union { // The first section of this enum are tags that require no payload. u8, i8, - isize, + u16, + i16, + u32, + i32, + u64, + i64, usize, + isize, c_short, c_ushort, c_int, @@ -1292,6 +1589,7 @@ pub const Type = extern union { single_const_pointer, int_signed, int_unsigned, + function, pub const last_no_payload_tag = Tag.const_slice_u8; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -1330,6 +1628,14 @@ pub const Type = extern union { bits: u16, }; + + pub const Function = struct { + base: Payload = Payload{ .tag = .function }, + + param_types: []Type, + return_type: Type, + cc: std.builtin.CallingConvention, + }; }; }; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index fc5854c40f..6509ee52f6 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -23,8 +23,14 @@ pub const Value = extern union { // The first section of this enum are tags that require no payload. u8_type, i8_type, - isize_type, + u16_type, + i16_type, + u32_type, + i32_type, + u64_type, + i64_type, usize_type, + isize_type, c_short_type, c_ushort_type, c_int_type, @@ -114,8 +120,14 @@ pub const Value = extern union { } else switch (self.ptr_otherwise.tag) { .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -222,6 +234,12 @@ pub const Value = extern union { while (true) switch (val.tag()) { .u8_type => return out_stream.writeAll("u8"), .i8_type => return out_stream.writeAll("i8"), + .u16_type => return out_stream.writeAll("u16"), + .i16_type => return out_stream.writeAll("i16"), + .u32_type => return out_stream.writeAll("u32"), + .i32_type => return out_stream.writeAll("i32"), + .u64_type => return out_stream.writeAll("u64"), + .i64_type => return out_stream.writeAll("i64"), .isize_type => return out_stream.writeAll("isize"), .usize_type => return out_stream.writeAll("usize"), .c_short_type => return out_stream.writeAll("c_short"), @@ -308,8 +326,14 @@ pub const Value = extern union { .u8_type => Type.initTag(.u8), .i8_type => Type.initTag(.i8), - .isize_type => Type.initTag(.isize), + .u16_type => Type.initTag(.u16), + .i16_type => Type.initTag(.i16), + .u32_type => Type.initTag(.u32), + .i32_type => Type.initTag(.i32), + .u64_type => Type.initTag(.u64), + .i64_type => Type.initTag(.i64), .usize_type => Type.initTag(.usize), + .isize_type => Type.initTag(.isize), .c_short_type => Type.initTag(.c_short), .c_ushort_type => Type.initTag(.c_ushort), .c_int_type => Type.initTag(.c_int), @@ -366,8 +390,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -426,8 +456,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -487,8 +523,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -553,8 +595,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -648,8 +696,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -705,8 +759,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -807,8 +867,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -870,8 +936,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, @@ -950,8 +1022,14 @@ pub const Value = extern union { .ty, .u8_type, .i8_type, - .isize_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, .usize_type, + .isize_type, .c_short_type, .c_ushort_type, .c_int_type, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 43c0eac197..92dbc66e2b 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -34,7 +34,13 @@ pub const Inst = struct { /// These names are used directly as the instruction names in the text format. pub const Tag = enum { + /// Function parameter value. + arg, + /// A labeled block of code, which can return a value. + block, breakpoint, + /// Same as `break` but without an operand; the operand is assumed to be the void value. + breakvoid, call, compileerror, /// Special case, has no textual representation. @@ -75,7 +81,10 @@ pub const Inst = struct { pub fn TagToType(tag: Tag) type { return switch (tag) { + .arg => Arg, + .block => Block, .breakpoint => Breakpoint, + .breakvoid => BreakVoid, .call => Call, .declref => DeclRef, .declref_str => DeclRefStr, @@ -115,6 +124,27 @@ pub const Inst = struct { return @fieldParentPtr(T, "base", base); } + pub const Arg = struct { + pub const base_tag = Tag.arg; + base: Inst, + + positionals: struct { + index: usize, + }, + kw_args: struct {}, + }; + + pub const Block = struct { + pub const base_tag = Tag.block; + base: Inst, + + positionals: struct { + label: []const u8, + body: Module.Body, + }, + kw_args: struct {}, + }; + pub const Breakpoint = struct { pub const base_tag = Tag.breakpoint; base: Inst, @@ -123,6 +153,16 @@ pub const Inst = struct { kw_args: struct {}, }; + pub const BreakVoid = struct { + pub const base_tag = Tag.breakvoid; + base: Inst, + + positionals: struct { + label: []const u8, + }, + kw_args: struct {}, + }; + pub const Call = struct { pub const base_tag = Tag.call; base: Inst, @@ -347,6 +387,14 @@ pub const Inst = struct { kw_args: struct {}, pub const Builtin = enum { + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, isize, usize, c_short, @@ -378,6 +426,14 @@ pub const Inst = struct { pub fn toTypedValue(self: Builtin) TypedValue { return switch (self) { + .i8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i8_type) }, + .u8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u8_type) }, + .i16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i16_type) }, + .u16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u16_type) }, + .i32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i32_type) }, + .u32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u32_type) }, + .i64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i64_type) }, + .u64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u64_type) }, .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) }, .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) }, .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) }, @@ -591,7 +647,10 @@ pub const Module = struct { ) @TypeOf(stream).Error!void { // TODO I tried implementing this with an inline for loop and hit a compiler bug switch (inst.tag) { + .arg => return self.writeInstToStreamGeneric(stream, .arg, inst, inst_table), + .block => return self.writeInstToStreamGeneric(stream, .block, inst, inst_table), .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, inst, inst_table), + .breakvoid => return self.writeInstToStreamGeneric(stream, .breakvoid, inst, inst_table), .call => return self.writeInstToStreamGeneric(stream, .call, inst, inst_table), .declref => return self.writeInstToStreamGeneric(stream, .declref, inst, inst_table), .declref_str => return self.writeInstToStreamGeneric(stream, .declref_str, inst, inst_table), @@ -691,7 +750,7 @@ pub const Module = struct { }, bool => return stream.writeByte("01"[@boolToInt(param)]), []u8, []const u8 => return std.zig.renderStringLiteral(param, stream), - BigIntConst => return stream.print("{}", .{param}), + BigIntConst, usize => return stream.print("{}", .{param}), TypedValue => unreachable, // this is a special case *IrModule.Decl => unreachable, // this is a special case else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), @@ -718,7 +777,7 @@ pub const Module = struct { }; pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module { - var global_name_map = std.StringHashMap(usize).init(allocator); + var global_name_map = std.StringHashMap(*Inst).init(allocator); defer global_name_map.deinit(); var parser: Parser = .{ @@ -752,22 +811,24 @@ const Parser = struct { i: usize, source: [:0]const u8, decls: std.ArrayListUnmanaged(*Decl), - global_name_map: *std.StringHashMap(usize), + global_name_map: *std.StringHashMap(*Inst), error_msg: ?ErrorMsg = null, unnamed_index: usize, const Body = struct { instructions: std.ArrayList(*Inst), - name_map: std.StringHashMap(usize), + name_map: *std.StringHashMap(*Inst), }; - fn parseBody(self: *Parser) !Module.Body { + fn parseBody(self: *Parser, body_ctx: ?*Body) !Module.Body { + var name_map = std.StringHashMap(*Inst).init(self.allocator); + defer name_map.deinit(); + var body_context = Body{ .instructions = std.ArrayList(*Inst).init(self.allocator), - .name_map = std.StringHashMap(usize).init(self.allocator), + .name_map = if (body_ctx) |bctx| bctx.name_map else &name_map, }; defer body_context.instructions.deinit(); - defer body_context.name_map.deinit(); try requireEatBytes(self, "{"); skipSpace(self); @@ -782,7 +843,7 @@ const Parser = struct { skipSpace(self); const decl = try parseInstruction(self, &body_context, ident); const ident_index = body_context.instructions.items.len; - if (try body_context.name_map.put(ident, ident_index)) |_| { + if (try body_context.name_map.put(ident, decl.inst)) |_| { return self.fail("redefinition of identifier '{}'", .{ident}); } try body_context.instructions.append(decl.inst); @@ -866,12 +927,12 @@ const Parser = struct { skipSpace(self); try requireEatBytes(self, "="); skipSpace(self); - const inst = try parseInstruction(self, null, ident); + const decl = try parseInstruction(self, null, ident); const ident_index = self.decls.items.len; - if (try self.global_name_map.put(ident, ident_index)) |_| { + if (try self.global_name_map.put(ident, decl.inst)) |_| { return self.fail("redefinition of identifier '{}'", .{ident}); } - try self.decls.append(self.allocator, inst); + try self.decls.append(self.allocator, decl); }, ' ', '\n' => self.i += 1, 0 => break, @@ -1032,7 +1093,7 @@ const Parser = struct { }; } switch (T) { - Module.Body => return parseBody(self), + Module.Body => return parseBody(self, body_ctx), bool => { const bool_value = switch (self.source[self.i]) { '0' => false, @@ -1060,6 +1121,10 @@ const Parser = struct { *Inst => return parseParameterInst(self, body_ctx), []u8, []const u8 => return self.parseStringLiteral(), BigIntConst => return self.parseIntegerLiteral(), + usize => { + const big_int = try self.parseIntegerLiteral(); + return big_int.to(usize) catch |err| return self.fail("integer literal: {}", .{@errorName(err)}); + }, TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}), *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}), else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), @@ -1075,7 +1140,7 @@ const Parser = struct { }; const map = if (local_ref) if (body_ctx) |bc| - &bc.name_map + bc.name_map else return self.fail("referencing a % instruction in global scope", .{}) else @@ -1107,11 +1172,7 @@ const Parser = struct { return &declval.base; } }; - if (local_ref) { - return body_ctx.?.instructions.items[kv.value]; - } else { - return self.decls.items[kv.value].inst; - } + return kv.value; } fn generateName(self: *Parser) ![]u8 { @@ -1456,7 +1517,7 @@ const EmitZIR = struct { fn emitBody( self: *EmitZIR, - body: IrModule.Body, + body: ir.Body, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), instructions: *std.ArrayList(*Inst), ) Allocator.Error!void { @@ -1466,6 +1527,57 @@ const EmitZIR = struct { }; for (body.instructions) |inst| { const new_inst = switch (inst.tag) { + .add => blk: { + const old_inst = inst.cast(ir.Inst.Add).?; + const new_inst = try self.arena.allocator.create(Inst.Add); + new_inst.* = .{ + .base = .{ + .src = inst.src, + .tag = Inst.Add.base_tag, + }, + .positionals = .{ + .lhs = try self.resolveInst(new_body, old_inst.args.lhs), + .rhs = try self.resolveInst(new_body, old_inst.args.rhs), + }, + .kw_args = .{}, + }; + break :blk &new_inst.base; + }, + .arg => blk: { + const old_inst = inst.cast(ir.Inst.Arg).?; + const new_inst = try self.arena.allocator.create(Inst.Arg); + new_inst.* = .{ + .base = .{ + .src = inst.src, + .tag = Inst.Arg.base_tag, + }, + .positionals = .{ .index = old_inst.args.index }, + .kw_args = .{}, + }; + break :blk &new_inst.base; + }, + .block => blk: { + const old_inst = inst.cast(ir.Inst.Block).?; + const new_inst = try self.arena.allocator.create(Inst.Block); + + var block_body = std.ArrayList(*Inst).init(self.allocator); + defer block_body.deinit(); + + try self.emitBody(old_inst.args.body, inst_table, &block_body); + + new_inst.* = .{ + .base = .{ + .src = inst.src, + .tag = Inst.Block.base_tag, + }, + .positionals = .{ + .label = try self.autoName(), + .body = .{ .instructions = block_body.toOwnedSlice() }, + }, + .kw_args = .{}, + }; + break :blk &new_inst.base; + }, .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), .call => blk: { const old_inst = inst.cast(ir.Inst.Call).?; @@ -1660,6 +1772,14 @@ const EmitZIR = struct { fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl { switch (ty.tag()) { + .i8 => return self.emitPrimitive(src, .i8), + .u8 => return self.emitPrimitive(src, .u8), + .i16 => return self.emitPrimitive(src, .i16), + .u16 => return self.emitPrimitive(src, .u16), + .i32 => return self.emitPrimitive(src, .i32), + .u32 => return self.emitPrimitive(src, .u32), + .i64 => return self.emitPrimitive(src, .i64), + .u64 => return self.emitPrimitive(src, .u64), .isize => return self.emitPrimitive(src, .isize), .usize => return self.emitPrimitive(src, .usize), .c_short => return self.emitPrimitive(src, .c_short),