authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-19 03:14:51-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-19 19:31:50-04:00
logf020999ca3135220a1b34e05d22c76ed3628b0c2
tree7474f66ada760b6876c0b3ba64b02a9558cce569
parent82e294cf09cab1610f948430f4c05150f3913806

ir: parse asm instructions


1 files changed, 80 insertions(+), 40 deletions(-)

src-self-hosted/ir.zig+80-40
...@@ -15,7 +15,7 @@ pub const Inst = struct {...@@ -15,7 +15,7 @@ pub const Inst = struct {
15 fieldptr,15 fieldptr,
16 deref,16 deref,
17 @"asm",17 @"asm",
18 unreach,18 @"unreachable",
19 @"fn",19 @"fn",
20 };20 };
2121
...@@ -26,7 +26,7 @@ pub const Inst = struct {...@@ -26,7 +26,7 @@ pub const Inst = struct {
26 .fieldptr => FieldPtr,26 .fieldptr => FieldPtr,
27 .deref => Deref,27 .deref => Deref,
28 .@"asm" => Assembly,28 .@"asm" => Assembly,
29 .unreach => Unreach,29 .@"unreachable" => Unreach,
30 .@"fn" => Fn,30 .@"fn" => Fn,
31 };31 };
32 }32 }
...@@ -75,8 +75,16 @@ pub const Inst = struct {...@@ -75,8 +75,16 @@ pub const Inst = struct {
75 pub const Assembly = struct {75 pub const Assembly = struct {
76 base: Inst = Inst{ .tag = .@"asm" },76 base: Inst = Inst{ .tag = .@"asm" },
7777
78 positionals: struct {},78 positionals: struct {
79 kw_args: struct {},79 asm_source: *Inst,
80 },
81 kw_args: struct {
82 @"volatile": bool = false,
83 output: ?*Inst = null,
84 inputs: []*Inst = &[0]*Inst{},
85 clobbers: []*Inst = &[0]*Inst{},
86 args: []*Inst = &[0]*Inst{},
87 },
80 };88 };
8189
82 pub const Unreach = struct {90 pub const Unreach = struct {
...@@ -174,7 +182,9 @@ fn eatByte(ctx: *ParseContext, byte: u8) bool {...@@ -174,7 +182,9 @@ fn eatByte(ctx: *ParseContext, byte: u8) bool {
174}182}
175183
176fn skipSpace(ctx: *ParseContext) void {184fn skipSpace(ctx: *ParseContext) void {
177 while (ctx.i < ctx.source.len and ctx.source[ctx.i] == ' ') : (ctx.i += 1) {}185 while (ctx.i < ctx.source.len and (ctx.source[ctx.i] == ' ' or ctx.source[ctx.i] == '\n')) {
186 ctx.i += 1;
187 }
178}188}
179189
180fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void {190fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void {
...@@ -284,14 +294,17 @@ fn parseInstructionGeneric(...@@ -284,14 +294,17 @@ fn parseInstructionGeneric(
284 skipSpace(ctx);294 skipSpace(ctx);
285 const name = try skipToAndOver(ctx, '=');295 const name = try skipToAndOver(ctx, '=');
286 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {296 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
287 if (mem.eql(u8, name, arg_field.name)) {297 const field_name = arg_field.name;
288 @field(inst_specific.kw_args, arg_field.name) = try parseParameterGeneric(298 if (mem.eql(u8, name, field_name)) {
289 ctx,299 const NonOptional = switch (@typeInfo(arg_field.field_type)) {
290 arg_field.field_type,300 .Optional => |info| info.child,
291 body_ctx,301 else => arg_field.field_type,
292 );302 };
303 @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(ctx, NonOptional, body_ctx);
293 break;304 break;
294 }305 }
306 } else {
307 return parseError(ctx, "unrecognized keyword parameter: '{}'", .{name});
295 }308 }
296 skipSpace(ctx);309 skipSpace(ctx);
297 }310 }
...@@ -317,45 +330,72 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type, body_ctx: ?*BodyC...@@ -317,45 +330,72 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type, body_ctx: ?*BodyC
317 }330 }
318 switch (T) {331 switch (T) {
319 Inst.Fn.Body => return parseBody(ctx),332 Inst.Fn.Body => return parseBody(ctx),
320 *Inst => {333 bool => {
321 const local_ref = switch (ctx.source[ctx.i]) {334 const bool_value = switch (ctx.source[ctx.i]) {
322 '@' => false,335 '0' => false,
323 '%' => true,336 '1' => true,
324 '"' => return parseStringLiteralConst(ctx, null),337 else => |byte| return parseError(ctx, "expected '0' or '1' for boolean value, found {c}", .{byte}),
325 else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
326 };338 };
327 const map = if (local_ref)
328 if (body_ctx) |bc|
329 &bc.name_map
330 else
331 return parseError(ctx, "referencing a % instruction in global scope", .{})
332 else
333 ctx.global_name_map;
334
335 ctx.i += 1;339 ctx.i += 1;
336 const name_start = ctx.i;340 return bool_value;
337 while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {341 },
338 ' ', '\n', ',', ')' => break,342 []*Inst => {
339 else => continue,343 try requireEatBytes(ctx, "[");
340 };344 skipSpace(ctx);
341 const ident = ctx.source[name_start..ctx.i];345 if (eatByte(ctx, ']')) return &[0]*Inst{};
342 const kv = map.get(ident) orelse {346
343 const bad_name = ctx.source[name_start - 1 .. ctx.i];347 var instructions = std.ArrayList(*Inst).init(ctx.allocator);
344 ctx.i = name_start - 1;348 defer instructions.deinit();
345 return parseError(ctx, "unrecognized identifier: {}", .{bad_name});349 while (true) {
346 };350 skipSpace(ctx);
347 if (local_ref) {351 try instructions.append(try parseParameterInst(ctx, body_ctx));
348 return body_ctx.?.instructions.items[kv.value];352 skipSpace(ctx);
349 } else {353 if (!eatByte(ctx, ',')) break;
350 return ctx.decls.items[kv.value];
351 }354 }
355 try requireEatBytes(ctx, "]");
356 return instructions.toOwnedSlice();
352 },357 },
358 *Inst => return parseParameterInst(ctx, body_ctx),
353 Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}),359 Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}),
354 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),360 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
355 }361 }
356 return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)});362 return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)});
357}363}
358364
365fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst {
366 const local_ref = switch (ctx.source[ctx.i]) {
367 '@' => false,
368 '%' => true,
369 '"' => return parseStringLiteralConst(ctx, null),
370 else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
371 };
372 const map = if (local_ref)
373 if (body_ctx) |bc|
374 &bc.name_map
375 else
376 return parseError(ctx, "referencing a % instruction in global scope", .{})
377 else
378 ctx.global_name_map;
379
380 ctx.i += 1;
381 const name_start = ctx.i;
382 while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {
383 ' ', '\n', ',', ')', ']' => break,
384 else => continue,
385 };
386 const ident = ctx.source[name_start..ctx.i];
387 const kv = map.get(ident) orelse {
388 const bad_name = ctx.source[name_start - 1 .. ctx.i];
389 ctx.i = name_start - 1;
390 return parseError(ctx, "unrecognized identifier: {}", .{bad_name});
391 };
392 if (local_ref) {
393 return body_ctx.?.instructions.items[kv.value];
394 } else {
395 return ctx.decls.items[kv.value];
396 }
397}
398
359const BodyContext = struct {399const BodyContext = struct {
360 instructions: std.ArrayList(*Inst),400 instructions: std.ArrayList(*Inst),
361 name_map: std.StringHashMap(usize),401 name_map: std.StringHashMap(usize),