diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 6f2db321ea..31453be845 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -14,6 +14,7 @@ pub const Inst = struct { Deref, Assembly, Unreach, + Fn, }; pub const Tag = enum { @@ -23,6 +24,7 @@ pub const Inst = struct { deref, @"asm", unreach, + @"fn", }; /// This struct owns the `Value` memory. When the struct is deallocated, @@ -31,26 +33,59 @@ pub const Inst = struct { pub const Constant = struct { base: Inst = Inst{ .tag = .constant }, value: *Value, + + positionals: struct {}, + kw_args: struct {}, }; pub const PtrToInt = struct { base: Inst = Inst{ .tag = .ptrtoint }, + + positionals: struct {}, + kw_args: struct {}, }; pub const FieldPtr = struct { base: Inst = Inst{ .tag = .fieldptr }, + + positionals: struct {}, + kw_args: struct {}, }; pub const Deref = struct { base: Inst = Inst{ .tag = .deref }, + + positionals: struct {}, + kw_args: struct {}, }; pub const Assembly = struct { base: Inst = Inst{ .tag = .@"asm" }, + + positionals: struct {}, + kw_args: struct {}, }; pub const Unreach = struct { base: Inst = Inst{ .tag = .unreach }, + + positionals: struct {}, + kw_args: struct {}, + }; + + pub const Fn = struct { + base: Inst = Inst{ .tag = .@"fn" }, + + positionals: struct { + body: Body, + }, + kw_args: struct { + cc: std.builtin.CallingConvention = .Unspecified, + }, + + pub const Body = struct { + instructions: []*Inst, + }; }; }; @@ -168,8 +203,45 @@ fn parseInstruction(ctx: *ParseContext) !*Inst { '0'...'9' => return parseIntegerLiteralConst(ctx), else => {}, } - const fn_name = skipToAndOver(ctx, '('); - return parseError(ctx, "TODO parse instruction '{}'", .{fn_name}); + const fn_name = try skipToAndOver(ctx, '('); + inline for (Inst.all_types) |InstType| { + const this_name = @tagName(std.meta.fieldInfo(InstType, "base").default_value.?.tag); + if (mem.eql(u8, this_name, fn_name)) { + return parseInstructionGeneric(ctx, this_name, InstType); + } + } + return parseError(ctx, "unknown instruction '{}'", .{fn_name}); +} + +fn parseInstructionGeneric(ctx: *ParseContext, comptime fn_name: []const u8, comptime InstType: type) !*Inst { + const inst_specific = try ctx.allocator.create(InstType); + + const Positionals = @TypeOf(inst_specific.positionals); + inline for (@typeInfo(Positionals).Struct.fields) |arg_field| { + @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(ctx, arg_field.field_type); + } + + const KW_Args = @TypeOf(inst_specific.kw_args); + inst_specific.kw_args = .{}; // assign defaults + skipSpace(ctx); + while (eatByte(ctx, ',')) { + skipSpace(ctx); + const name = try skipToAndOver(ctx, '='); + inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| { + if (mem.eql(u8, name, arg_field.name)) { + @field(inst_specific.kw_args, arg_field.name) = try parseParameterGeneric(ctx, arg_field.field_type); + break; + } + } + skipSpace(ctx); + } + try requireEatBytes(ctx, ")"); + + return &inst_specific.base; +} + +fn parseParameterGeneric(ctx: *ParseContext, comptime T: type) !T { + return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)}); } fn parseStringLiteralConst(ctx: *ParseContext) !*Inst { @@ -192,7 +264,11 @@ fn parseStringLiteralConst(ctx: *ParseContext) !*Inst { const bytes_val = try ctx.allocator.create(Value.Bytes); bytes_val.* = .{ .data = parsed }; const const_inst = try ctx.allocator.create(Inst.Constant); - const_inst.* = .{ .value = &bytes_val.base }; + const_inst.* = .{ + .value = &bytes_val.base, + .positionals = .{}, + .kw_args = .{}, + }; return &const_inst.base; }, '\\' => { diff --git a/test/stage2/ir.zig b/test/stage2/ir.zig index e948e7a8b8..498db3a4eb 100644 --- a/test/stage2/ir.zig +++ b/test/stage2/ir.zig @@ -25,7 +25,7 @@ test "hello world IR" { \\ args=[%6, %7]) \\ \\ %9 = unreachable() - \\}, cc=naked); + \\}, cc=naked) \\ \\@2 = export("_start", @1) ,