ir: parse asm instructions

master
Andrew Kelley 2020-04-19 03:14:51 -04:00
parent 82e294cf09
commit f020999ca3
1 changed files with 81 additions and 41 deletions

View File

@ -15,7 +15,7 @@ pub const Inst = struct {
fieldptr, fieldptr,
deref, deref,
@"asm", @"asm",
unreach, @"unreachable",
@"fn", @"fn",
}; };
@ -26,7 +26,7 @@ pub const Inst = struct {
.fieldptr => FieldPtr, .fieldptr => FieldPtr,
.deref => Deref, .deref => Deref,
.@"asm" => Assembly, .@"asm" => Assembly,
.unreach => Unreach, .@"unreachable" => Unreach,
.@"fn" => Fn, .@"fn" => Fn,
}; };
} }
@ -75,8 +75,16 @@ pub const Inst = struct {
pub const Assembly = struct { pub const Assembly = struct {
base: Inst = Inst{ .tag = .@"asm" }, base: Inst = Inst{ .tag = .@"asm" },
positionals: struct {}, positionals: struct {
kw_args: 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 { pub const Unreach = struct {
@ -174,7 +182,9 @@ fn eatByte(ctx: *ParseContext, byte: u8) bool {
} }
fn skipSpace(ctx: *ParseContext) void { 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 { fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void {
@ -284,14 +294,17 @@ fn parseInstructionGeneric(
skipSpace(ctx); skipSpace(ctx);
const name = try skipToAndOver(ctx, '='); const name = try skipToAndOver(ctx, '=');
inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| { inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
if (mem.eql(u8, name, arg_field.name)) { const field_name = arg_field.name;
@field(inst_specific.kw_args, arg_field.name) = try parseParameterGeneric( if (mem.eql(u8, name, field_name)) {
ctx, const NonOptional = switch (@typeInfo(arg_field.field_type)) {
arg_field.field_type, .Optional => |info| info.child,
body_ctx, else => arg_field.field_type,
); };
@field(inst_specific.kw_args, field_name) = try parseParameterGeneric(ctx, NonOptional, body_ctx);
break; break;
} }
} else {
return parseError(ctx, "unrecognized keyword parameter: '{}'", .{name});
} }
skipSpace(ctx); skipSpace(ctx);
} }
@ -317,45 +330,72 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type, body_ctx: ?*BodyC
} }
switch (T) { switch (T) {
Inst.Fn.Body => return parseBody(ctx), Inst.Fn.Body => return parseBody(ctx),
*Inst => { bool => {
const local_ref = switch (ctx.source[ctx.i]) { const bool_value = switch (ctx.source[ctx.i]) {
'@' => false, '0' => false,
'%' => true, '1' => true,
'"' => return parseStringLiteralConst(ctx, null), else => |byte| return parseError(ctx, "expected '0' or '1' for boolean value, found {c}", .{byte}),
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; ctx.i += 1;
const name_start = ctx.i; return bool_value;
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];
}
}, },
[]*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", .{}), Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}),
else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
} }
return parseError(ctx, "TODO parse parameter {}", .{@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 { const BodyContext = struct {
instructions: std.ArrayList(*Inst), instructions: std.ArrayList(*Inst),
name_map: std.StringHashMap(usize), name_map: std.StringHashMap(usize),