Commit f020999ca3

Andrew Kelley <andrew@ziglang.org>
2020-04-19 09:14:51
ir: parse asm instructions
1 parent 82e294c
Changed files (1)
src-self-hosted
src-self-hosted/ir.zig
@@ -15,7 +15,7 @@ pub const Inst = struct {
         fieldptr,
         deref,
         @"asm",
-        unreach,
+        @"unreachable",
         @"fn",
     };
 
@@ -26,7 +26,7 @@ pub const Inst = struct {
             .fieldptr => FieldPtr,
             .deref => Deref,
             .@"asm" => Assembly,
-            .unreach => Unreach,
+            .@"unreachable" => Unreach,
             .@"fn" => Fn,
         };
     }
@@ -75,8 +75,16 @@ pub const Inst = struct {
     pub const Assembly = struct {
         base: Inst = Inst{ .tag = .@"asm" },
 
-        positionals: struct {},
-        kw_args: struct {},
+        positionals: struct {
+            asm_source: *Inst,
+        },
+        kw_args: struct {
+            @"volatile": bool = false,
+            output: ?*Inst = null,
+            inputs: []*Inst = &[0]*Inst{},
+            clobbers: []*Inst = &[0]*Inst{},
+            args: []*Inst = &[0]*Inst{},
+        },
     };
 
     pub const Unreach = struct {
@@ -174,7 +182,9 @@ fn eatByte(ctx: *ParseContext, byte: u8) bool {
 }
 
 fn skipSpace(ctx: *ParseContext) void {
-    while (ctx.i < ctx.source.len and ctx.source[ctx.i] == ' ') : (ctx.i += 1) {}
+    while (ctx.i < ctx.source.len and (ctx.source[ctx.i] == ' ' or ctx.source[ctx.i] == '\n')) {
+        ctx.i += 1;
+    }
 }
 
 fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void {
@@ -284,14 +294,17 @@ fn parseInstructionGeneric(
         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,
-                    body_ctx,
-                );
+            const field_name = arg_field.name;
+            if (mem.eql(u8, name, field_name)) {
+                const NonOptional = switch (@typeInfo(arg_field.field_type)) {
+                    .Optional => |info| info.child,
+                    else => arg_field.field_type,
+                };
+                @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(ctx, NonOptional, body_ctx);
                 break;
             }
+        } else {
+            return parseError(ctx, "unrecognized keyword parameter: '{}'", .{name});
         }
         skipSpace(ctx);
     }
@@ -317,45 +330,72 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type, body_ctx: ?*BodyC
     }
     switch (T) {
         Inst.Fn.Body => return parseBody(ctx),
-        *Inst => {
-            const local_ref = switch (ctx.source[ctx.i]) {
-                '@' => false,
-                '%' => true,
-                '"' => return parseStringLiteralConst(ctx, null),
-                else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
+        bool => {
+            const bool_value = switch (ctx.source[ctx.i]) {
+                '0' => false,
+                '1' => true,
+                else => |byte| return parseError(ctx, "expected '0' or '1' for boolean value, found {c}", .{byte}),
             };
-            const map = if (local_ref)
-                if (body_ctx) |bc|
-                    &bc.name_map
-                else
-                    return parseError(ctx, "referencing a % instruction in global scope", .{})
-            else
-                ctx.global_name_map;
-
             ctx.i += 1;
-            const name_start = ctx.i;
-            while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {
-                ' ', '\n', ',', ')' => break,
-                else => continue,
-            };
-            const ident = ctx.source[name_start..ctx.i];
-            const kv = map.get(ident) orelse {
-                const bad_name = ctx.source[name_start - 1 .. ctx.i];
-                ctx.i = name_start - 1;
-                return parseError(ctx, "unrecognized identifier: {}", .{bad_name});
-            };
-            if (local_ref) {
-                return body_ctx.?.instructions.items[kv.value];
-            } else {
-                return ctx.decls.items[kv.value];
+            return bool_value;
+        },
+        []*Inst => {
+            try requireEatBytes(ctx, "[");
+            skipSpace(ctx);
+            if (eatByte(ctx, ']')) return &[0]*Inst{};
+
+            var instructions = std.ArrayList(*Inst).init(ctx.allocator);
+            defer instructions.deinit();
+            while (true) {
+                skipSpace(ctx);
+                try instructions.append(try parseParameterInst(ctx, body_ctx));
+                skipSpace(ctx);
+                if (!eatByte(ctx, ',')) break;
             }
+            try requireEatBytes(ctx, "]");
+            return instructions.toOwnedSlice();
         },
+        *Inst => return parseParameterInst(ctx, body_ctx),
         Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}),
         else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
     }
     return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)});
 }
 
+fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst {
+    const local_ref = switch (ctx.source[ctx.i]) {
+        '@' => false,
+        '%' => true,
+        '"' => return parseStringLiteralConst(ctx, null),
+        else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
+    };
+    const map = if (local_ref)
+        if (body_ctx) |bc|
+            &bc.name_map
+        else
+            return parseError(ctx, "referencing a % instruction in global scope", .{})
+    else
+        ctx.global_name_map;
+
+    ctx.i += 1;
+    const name_start = ctx.i;
+    while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {
+        ' ', '\n', ',', ')', ']' => break,
+        else => continue,
+    };
+    const ident = ctx.source[name_start..ctx.i];
+    const kv = map.get(ident) orelse {
+        const bad_name = ctx.source[name_start - 1 .. ctx.i];
+        ctx.i = name_start - 1;
+        return parseError(ctx, "unrecognized identifier: {}", .{bad_name});
+    };
+    if (local_ref) {
+        return body_ctx.?.instructions.items[kv.value];
+    } else {
+        return ctx.decls.items[kv.value];
+    }
+}
+
 const BodyContext = struct {
     instructions: std.ArrayList(*Inst),
     name_map: std.StringHashMap(usize),