Refactor entire wasm-backend to use new AIR memory layout
This commit is contained in:
committed by
Andrew Kelley
parent
424f260f85
commit
2438f61f1c
@@ -483,8 +483,8 @@ pub const Result = union(enum) {
|
||||
externally_managed: []const u8,
|
||||
};
|
||||
|
||||
/// Hashmap to store generated `WValue` for each `Inst`
|
||||
pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
|
||||
/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
|
||||
pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue);
|
||||
|
||||
/// Code represents the `Code` section of wasm that
|
||||
/// belongs to a function
|
||||
@@ -495,7 +495,7 @@ pub const Context = struct {
|
||||
air: Air,
|
||||
liveness: Liveness,
|
||||
gpa: *mem.Allocator,
|
||||
/// Table to save `WValue`'s generated by an `Inst`
|
||||
/// Table to save `WValue`'s generated by an `Air.Inst`
|
||||
values: ValueTable,
|
||||
/// Mapping from Air.Inst.Index to block ids
|
||||
blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
|
||||
@@ -547,14 +547,15 @@ pub const Context = struct {
|
||||
|
||||
/// Resolves the `WValue` for the given instruction `inst`
|
||||
/// When the given instruction has a `Value`, it returns a constant instead
|
||||
fn resolveInst(self: Context, inst: Air.Inst) Index {
|
||||
if (!inst.ty.hasCodeGenBits()) return .none;
|
||||
fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue {
|
||||
const ref_type = self.air.getRefType(ref);
|
||||
if (ref_type.hasCodeGenBits()) return .none;
|
||||
|
||||
if (inst.value()) |_| {
|
||||
return WValue{ .constant = inst };
|
||||
if (self.air.instructions.items(.tag)[@enumToInt(ref)] == .constant) {
|
||||
return WValue{ .constant = @enumToInt(ref) };
|
||||
}
|
||||
|
||||
return self.values.get(inst).?; // Instruction does not dominate all uses!
|
||||
return self.values.get(ref).?; // Instruction does not dominate all uses!
|
||||
}
|
||||
|
||||
/// Using a given `Type`, returns the corresponding wasm Valtype
|
||||
@@ -610,7 +611,12 @@ pub const Context = struct {
|
||||
try writer.writeByte(wasm.opcode(.local_get));
|
||||
try leb.writeULEB128(writer, idx);
|
||||
},
|
||||
.constant => |inst| try self.emitConstant(inst.value().?, inst.ty), // creates a new constant onto the stack
|
||||
.constant => |index| {
|
||||
const ty_pl = self.air.instructions.items(.data)[index].ty_pl;
|
||||
const value = self.air.values[ty_pl.payload];
|
||||
// create a new constant onto the stack
|
||||
try self.emitConstant(value, self.air.getRefType(ty_pl.ty));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,10 +632,7 @@ pub const Context = struct {
|
||||
const fields_len = @intCast(u32, struct_data.fields.count());
|
||||
try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
|
||||
for (struct_data.fields.values()) |*value| {
|
||||
const val_type = try self.genValtype(
|
||||
.{ .node_offset = struct_data.node_offset },
|
||||
value.ty,
|
||||
);
|
||||
const val_type = try self.genValtype(value.ty);
|
||||
self.locals.appendAssumeCapacity(val_type);
|
||||
self.local_index += 1;
|
||||
}
|
||||
@@ -640,7 +643,7 @@ pub const Context = struct {
|
||||
},
|
||||
.ErrorUnion => {
|
||||
const payload_type = ty.errorUnionChild();
|
||||
const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type);
|
||||
const val_type = try self.genValtype(payload_type);
|
||||
|
||||
// we emit the error value as the first local, and the payload as the following.
|
||||
// The first local is also used to find the index of the error and payload.
|
||||
@@ -657,7 +660,7 @@ pub const Context = struct {
|
||||
} };
|
||||
},
|
||||
else => {
|
||||
const valtype = try self.genValtype(.{ .node_offset = 0 }, ty);
|
||||
const valtype = try self.genValtype(ty);
|
||||
try self.locals.append(self.gpa, valtype);
|
||||
self.local_index += 1;
|
||||
return WValue{ .local = initial_index };
|
||||
@@ -708,8 +711,7 @@ pub const Context = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn genFunc(self: *Context, func: *Module.Fn) InnerError!Result {
|
||||
_ = func;
|
||||
pub fn genFunc(self: *Context) InnerError!Result {
|
||||
try self.genFunctype();
|
||||
// TODO: check for and handle death of instructions
|
||||
|
||||
@@ -790,44 +792,43 @@ pub const Context = struct {
|
||||
fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {
|
||||
const air_tags = self.air.instructions.items(.tag);
|
||||
return switch (air_tags[inst]) {
|
||||
// .add => self.genBinOp(inst.castTag(.add).?, .add),
|
||||
// .alloc => self.genAlloc(inst.castTag(.alloc).?),
|
||||
// .arg => self.genArg(inst.castTag(.arg).?),
|
||||
// .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
|
||||
// .bitcast => self.genBitcast(inst.castTag(.bitcast).?),
|
||||
// .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
|
||||
// .block => self.genBlock(inst.castTag(.block).?),
|
||||
// .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
|
||||
// .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
|
||||
// .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
|
||||
// .br => self.genBr(inst.castTag(.br).?),
|
||||
// .call => self.genCall(inst.castTag(.call).?),
|
||||
// .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
|
||||
// .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
|
||||
// .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
|
||||
// .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),
|
||||
// .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),
|
||||
// .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),
|
||||
// .condbr => self.genCondBr(inst.castTag(.condbr).?),
|
||||
// .constant => unreachable,
|
||||
// .dbg_stmt => WValue.none,
|
||||
// .div => self.genBinOp(inst.castTag(.div).?, .div),
|
||||
// .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),
|
||||
// .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),
|
||||
// .load => self.genLoad(inst.castTag(.load).?),
|
||||
// .loop => self.genLoop(inst.castTag(.loop).?),
|
||||
// .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
|
||||
// .not => self.genNot(inst.castTag(.not).?),
|
||||
// .ret => self.genRet(inst.castTag(.ret).?),
|
||||
// .retvoid => WValue.none,
|
||||
// .store => self.genStore(inst.castTag(.store).?),
|
||||
// .struct_field_ptr => self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
|
||||
// .sub => self.genBinOp(inst.castTag(.sub).?, .sub),
|
||||
// .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?),
|
||||
// .unreach => self.genUnreachable(inst.castTag(.unreach).?),
|
||||
// .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?),
|
||||
// .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
|
||||
// .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
|
||||
.add => self.genBinOp(inst, .add),
|
||||
.alloc => self.genAlloc(inst),
|
||||
.arg => self.genArg(inst),
|
||||
.bit_and => self.genBinOp(inst, .@"and"),
|
||||
.bitcast => self.genBitcast(inst),
|
||||
.bit_or => self.genBinOp(inst, .@"or"),
|
||||
.block => self.genBlock(inst),
|
||||
.bool_and => self.genBinOp(inst, .@"and"),
|
||||
.bool_or => self.genBinOp(inst, .@"or"),
|
||||
.breakpoint => self.genBreakpoint(inst),
|
||||
.br => self.genBr(inst),
|
||||
.call => self.genCall(inst),
|
||||
.cmp_eq => self.genCmp(inst, .eq),
|
||||
.cmp_gte => self.genCmp(inst, .gte),
|
||||
.cmp_gt => self.genCmp(inst, .gt),
|
||||
.cmp_lte => self.genCmp(inst, .lte),
|
||||
.cmp_lt => self.genCmp(inst, .lt),
|
||||
.cmp_neq => self.genCmp(inst, .neq),
|
||||
.cond_br => self.genCondBr(inst),
|
||||
.constant => unreachable,
|
||||
.dbg_stmt => WValue.none,
|
||||
.div => self.genBinOp(inst, .div),
|
||||
.is_err => self.genIsErr(inst, .i32_ne),
|
||||
.is_non_err => self.genIsErr(inst, .i32_eq),
|
||||
.load => self.genLoad(inst),
|
||||
.loop => self.genLoop(inst),
|
||||
.mul => self.genBinOp(inst, .mul),
|
||||
.not => self.genNot(inst),
|
||||
.ret => self.genRet(inst),
|
||||
.store => self.genStore(inst),
|
||||
.struct_field_ptr => self.genStructFieldPtr(inst),
|
||||
.sub => self.genBinOp(inst, .sub),
|
||||
.switch_br => self.genSwitchBr(inst),
|
||||
.unreach => self.genUnreachable(inst),
|
||||
.unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst),
|
||||
.wrap_errunion_payload => self.genWrapErrUnionPayload(inst),
|
||||
.xor => self.genBinOp(inst, .xor),
|
||||
else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
|
||||
};
|
||||
}
|
||||
@@ -835,22 +836,27 @@ pub const Context = struct {
|
||||
fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {
|
||||
for (body) |inst| {
|
||||
const result = try self.genInst(inst);
|
||||
try self.values.putNoClobber(self.gpa, inst, result);
|
||||
try self.values.putNoClobber(self.gpa, @intToEnum(Air.Inst.Ref, inst), result);
|
||||
}
|
||||
}
|
||||
|
||||
fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
// TODO: Implement tail calls
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const operand = self.resolveInst(un_op);
|
||||
try self.emitWValue(operand);
|
||||
try self.code.append(wasm.opcode(.@"return"));
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const func_val = inst.func.value().?;
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = self.air.extraData(Air.Call, pl_op.payload);
|
||||
const args = self.air.extra[extra.end..][0..extra.data.args_len];
|
||||
|
||||
const target: *Decl = blk: {
|
||||
const ty_pl = self.air.instructions.items(.data)[@enumToInt(pl_op.operand)].ty_pl;
|
||||
const func_val = self.air.values[ty_pl.payload];
|
||||
|
||||
if (func_val.castTag(.function)) |func| {
|
||||
break :blk func.data.owner_decl;
|
||||
} else if (func_val.castTag(.extern_fn)) |ext_fn| {
|
||||
@@ -859,8 +865,8 @@ pub const Context = struct {
|
||||
return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
|
||||
};
|
||||
|
||||
for (inst.args) |arg| {
|
||||
const arg_val = self.resolveInst(arg);
|
||||
for (args) |arg| {
|
||||
const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg));
|
||||
try self.emitWValue(arg_val);
|
||||
}
|
||||
|
||||
@@ -877,15 +883,16 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const elem_type = inst.base.ty.elemType();
|
||||
const elem_type = self.air.getType(inst).elemType();
|
||||
return self.allocLocal(elem_type);
|
||||
}
|
||||
|
||||
fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const writer = self.code.writer();
|
||||
|
||||
const lhs = self.resolveInst(inst.lhs);
|
||||
const rhs = self.resolveInst(inst.rhs);
|
||||
const lhs = self.resolveInst(bin_op.lhs);
|
||||
const rhs = self.resolveInst(bin_op.rhs);
|
||||
|
||||
switch (lhs) {
|
||||
.multi_value => |multi_value| switch (rhs) {
|
||||
@@ -893,7 +900,7 @@ pub const Context = struct {
|
||||
// we simply assign the local_index to the rhs one.
|
||||
// This allows us to update struct fields without having to individually
|
||||
// set each local as each field's index will be calculated off the struct's base index
|
||||
.multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
|
||||
.multi_value => self.values.put(self.gpa, bin_op.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
|
||||
.constant, .none => {
|
||||
// emit all values onto the stack if constant
|
||||
try self.emitWValue(rhs);
|
||||
@@ -920,7 +927,8 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
return self.resolveInst(inst.operand);
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
return self.resolveInst(ty_op.operand);
|
||||
}
|
||||
|
||||
fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
@@ -931,8 +939,9 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
|
||||
const lhs = self.resolveInst(inst.lhs);
|
||||
const rhs = self.resolveInst(inst.rhs);
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const lhs = self.resolveInst(bin_op.lhs);
|
||||
const rhs = self.resolveInst(bin_op.rhs);
|
||||
|
||||
// it's possible for both lhs and/or rhs to return an offset as well,
|
||||
// in which case we return the first offset occurance we find.
|
||||
@@ -945,10 +954,11 @@ pub const Context = struct {
|
||||
try self.emitWValue(lhs);
|
||||
try self.emitWValue(rhs);
|
||||
|
||||
const bin_ty = self.air.getRefType(bin_op.lhs);
|
||||
const opcode: wasm.Opcode = buildOpcode(.{
|
||||
.op = op,
|
||||
.valtype1 = try self.typeToValtype(inst.base.ty),
|
||||
.signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned,
|
||||
.valtype1 = try self.typeToValtype(bin_ty),
|
||||
.signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
|
||||
});
|
||||
try self.code.append(wasm.opcode(opcode));
|
||||
return WValue{ .code_offset = offset };
|
||||
@@ -1064,14 +1074,17 @@ pub const Context = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn genBlock(self: *Context, block: Air.Inst.Index) InnerError!WValue {
|
||||
const block_ty = try self.genBlockType(block.base.ty);
|
||||
fn genBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));
|
||||
const extra = self.air.extraData(Air.Block, ty_pl.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
|
||||
try self.startBlock(.block, block_ty, null);
|
||||
// Here we set the current block idx, so breaks know the depth to jump
|
||||
// to when breaking out.
|
||||
try self.blocks.putNoClobber(self.gpa, block, self.block_depth);
|
||||
try self.genBody(block.body);
|
||||
try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
|
||||
try self.genBody(body);
|
||||
try self.endBlock();
|
||||
|
||||
return .none;
|
||||
@@ -1095,11 +1108,15 @@ pub const Context = struct {
|
||||
self.block_depth -= 1;
|
||||
}
|
||||
|
||||
fn genLoop(self: *Context, loop: Air.Inst.Index) InnerError!WValue {
|
||||
const loop_ty = try self.genBlockType(loop.base.ty);
|
||||
fn genLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const loop = self.air.extraData(Air.Block, ty_pl.payload);
|
||||
const body = self.air.extra[loop.end..][0..loop.data.body_len];
|
||||
|
||||
try self.startBlock(.loop, loop_ty, null);
|
||||
try self.genBody(loop.body);
|
||||
// result type of loop is always 'noreturn', meaning we can always
|
||||
// emit the wasm type 'block_empty'.
|
||||
try self.startBlock(.loop, wasm.block_empty, null);
|
||||
try self.genBody(body);
|
||||
|
||||
// breaking to the index of a loop block will continue the loop instead
|
||||
try self.code.append(wasm.opcode(.br));
|
||||
@@ -1110,8 +1127,12 @@ pub const Context = struct {
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genCondBr(self: *Context, condbr: Air.Inst.Index) InnerError!WValue {
|
||||
const condition = self.resolveInst(condbr.condition);
|
||||
fn genCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const condition = self.resolveInst(pl_op.operand);
|
||||
const extra = self.air.extraData(Air.CondBr, pl_op.payload);
|
||||
const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
|
||||
const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
|
||||
const writer = self.code.writer();
|
||||
|
||||
// TODO: Handle death instructions for then and else body
|
||||
@@ -1126,8 +1147,9 @@ pub const Context = struct {
|
||||
break :blk offset;
|
||||
},
|
||||
};
|
||||
const block_ty = try self.genBlockType(condbr.base.ty);
|
||||
try self.startBlock(.block, block_ty, offset);
|
||||
|
||||
// result type is always noreturn, so use `block_empty` as type.
|
||||
try self.startBlock(.block, wasm.block_empty, offset);
|
||||
|
||||
// we inserted the block in front of the condition
|
||||
// so now check if condition matches. If not, break outside this block
|
||||
@@ -1135,11 +1157,11 @@ pub const Context = struct {
|
||||
try writer.writeByte(wasm.opcode(.br_if));
|
||||
try leb.writeULEB128(writer, @as(u32, 0));
|
||||
|
||||
try self.genBody(condbr.else_body);
|
||||
try self.genBody(else_body);
|
||||
try self.endBlock();
|
||||
|
||||
// Outer block that matches the condition
|
||||
try self.genBody(condbr.then_body);
|
||||
try self.genBody(then_body);
|
||||
|
||||
return .none;
|
||||
}
|
||||
@@ -1149,21 +1171,23 @@ pub const Context = struct {
|
||||
// the comparison that we can later jump back to
|
||||
const offset = self.code.items.len;
|
||||
|
||||
const lhs = self.resolveInst(inst.lhs);
|
||||
const rhs = self.resolveInst(inst.rhs);
|
||||
const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
|
||||
const lhs = self.resolveInst(data.bin_op.lhs);
|
||||
const rhs = self.resolveInst(data.bin_op.rhs);
|
||||
const lhs_ty = self.air.getRefType(data.bin_op.lhs);
|
||||
|
||||
try self.emitWValue(lhs);
|
||||
try self.emitWValue(rhs);
|
||||
|
||||
const signedness: std.builtin.Signedness = blk: {
|
||||
// by default we tell the operand type is unsigned (i.e. bools and enum values)
|
||||
if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned;
|
||||
if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
|
||||
|
||||
// incase of an actual integer, we emit the correct signedness
|
||||
break :blk inst.lhs.ty.intInfo(self.target).signedness;
|
||||
break :blk lhs_ty.intInfo(self.target).signedness;
|
||||
};
|
||||
const opcode: wasm.Opcode = buildOpcode(.{
|
||||
.valtype1 = try self.typeToValtype(inst.lhs.ty),
|
||||
.valtype1 = try self.typeToValtype(lhs_ty),
|
||||
.op = switch (op) {
|
||||
.lt => .lt,
|
||||
.lte => .le,
|
||||
@@ -1178,16 +1202,17 @@ pub const Context = struct {
|
||||
return WValue{ .code_offset = offset };
|
||||
}
|
||||
|
||||
fn genBr(self: *Context, br: Air.Inst.Index) InnerError!WValue {
|
||||
fn genBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const br = self.air.instructions.items(.data)[inst].br;
|
||||
|
||||
// if operand has codegen bits we should break with a value
|
||||
if (br.operand.ty.hasCodeGenBits()) {
|
||||
const operand = self.resolveInst(br.operand);
|
||||
try self.emitWValue(operand);
|
||||
if (self.air.getRefType(br.operand).hasCodeGenBits()) {
|
||||
try self.emitWValue(self.resolveInst(br.operand));
|
||||
}
|
||||
|
||||
// We map every block to its block index.
|
||||
// We then determine how far we have to jump to it by substracting it from current block depth
|
||||
const idx: u32 = self.block_depth - self.blocks.get(br.block).?;
|
||||
const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
|
||||
const writer = self.code.writer();
|
||||
try writer.writeByte(wasm.opcode(.br));
|
||||
try leb.writeULEB128(writer, idx);
|
||||
@@ -1195,10 +1220,11 @@ pub const Context = struct {
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genNot(self: *Context, not: Air.Inst.Index) InnerError!WValue {
|
||||
fn genNot(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const offset = self.code.items.len;
|
||||
|
||||
const operand = self.resolveInst(not.operand);
|
||||
const operand = self.resolveInst(ty_op.operand);
|
||||
try self.emitWValue(operand);
|
||||
|
||||
// wasm does not have booleans nor the `not` instruction, therefore compare with 0
|
||||
@@ -1212,35 +1238,44 @@ pub const Context = struct {
|
||||
return WValue{ .code_offset = offset };
|
||||
}
|
||||
|
||||
fn genBreakpoint(self: *Context, breakpoint: Air.Inst.Index) InnerError!WValue {
|
||||
fn genBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
_ = self;
|
||||
_ = breakpoint;
|
||||
_ = inst;
|
||||
// unsupported by wasm itself. Can be implemented once we support DWARF
|
||||
// for wasm
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genUnreachable(self: *Context, unreach: Air.Inst.Index) InnerError!WValue {
|
||||
_ = unreach;
|
||||
fn genUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
_ = inst;
|
||||
try self.code.append(wasm.opcode(.@"unreachable"));
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genBitcast(self: *Context, bitcast: Air.Inst.Index) InnerError!WValue {
|
||||
return self.resolveInst(bitcast.operand);
|
||||
fn genBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
return self.resolveInst(ty_op.operand);
|
||||
}
|
||||
|
||||
fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const struct_ptr = self.resolveInst(inst.struct_ptr);
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.StructField, ty_pl.payload);
|
||||
const struct_ptr = self.resolveInst(extra.data.struct_ptr);
|
||||
|
||||
return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) };
|
||||
return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) };
|
||||
}
|
||||
|
||||
fn genSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const target = self.resolveInst(inst.target);
|
||||
const target_ty = inst.target.ty;
|
||||
const valtype = try self.typeToValtype(.{ .node_offset = 0 }, target_ty);
|
||||
const blocktype = try self.genBlockType(inst.base.ty);
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = self.air.extraData(Air.SwitchBr, pl_op.payload);
|
||||
const cases = self.air.extra[extra.end..][0..extra.data.cases_len];
|
||||
const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len];
|
||||
|
||||
const target = self.resolveInst(pl_op.operand);
|
||||
const target_ty = self.air.getRefType(pl_op.operand);
|
||||
const valtype = try self.typeToValtype(target_ty);
|
||||
// result type is always 'noreturn'
|
||||
const blocktype = wasm.block_empty;
|
||||
|
||||
const signedness: std.builtin.Signedness = blk: {
|
||||
// by default we tell the operand type is unsigned (i.e. bools and enum values)
|
||||
@@ -1249,11 +1284,18 @@ pub const Context = struct {
|
||||
// incase of an actual integer, we emit the correct signedness
|
||||
break :blk target_ty.intInfo(self.target).signedness;
|
||||
};
|
||||
for (inst.cases) |case| {
|
||||
for (cases) |case_idx| {
|
||||
const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
|
||||
const case_body = self.air.extra[case.end..][0..case.data.body_len];
|
||||
|
||||
// create a block for each case, when the condition does not match we break out of it
|
||||
try self.startBlock(.block, blocktype, null);
|
||||
try self.emitWValue(target);
|
||||
try self.emitConstant(.{ .node_offset = 0 }, case.item, target_ty);
|
||||
|
||||
// cases must represent a constant of which its type is in the `typed_value_map`
|
||||
// Therefore we can simply retrieve it.
|
||||
const ty_val = Air.Inst.Ref.typed_value_map[@enumToInt(case.data.item)];
|
||||
try self.emitConstant(ty_val.val, target_ty);
|
||||
const opcode = buildOpcode(.{
|
||||
.valtype1 = valtype,
|
||||
.op = .ne, // not equal because we jump out the block if it does not match the condition
|
||||
@@ -1264,7 +1306,7 @@ pub const Context = struct {
|
||||
try leb.writeULEB128(self.code.writer(), @as(u32, 0));
|
||||
|
||||
// emit our block code
|
||||
try self.genBody(case.body);
|
||||
try self.genBody(case_body);
|
||||
|
||||
// end the block we created earlier
|
||||
try self.endBlock();
|
||||
@@ -1272,13 +1314,14 @@ pub const Context = struct {
|
||||
|
||||
// finally, emit the else case if it exists. Here we will not have to
|
||||
// check for a condition, so also no need to emit a block.
|
||||
try self.genBody(inst.else_body);
|
||||
try self.genBody(else_body);
|
||||
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const operand = self.resolveInst(un_op);
|
||||
const offset = self.code.items.len;
|
||||
const writer = self.code.writer();
|
||||
|
||||
@@ -1294,7 +1337,8 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const operand = self.resolveInst(ty_op.operand);
|
||||
// The index of multi_value contains the error code. To get the initial index of the payload we get
|
||||
// the following index. Next, convert it to a `WValue.local`
|
||||
//
|
||||
@@ -1303,6 +1347,7 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
|
||||
return self.resolveInst(inst.operand);
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
return self.resolveInst(ty_op.operand);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -220,7 +220,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
|
||||
defer context.deinit();
|
||||
|
||||
// generate the 'code' section for the function declaration
|
||||
const result = context.genFunc(func) catch |err| switch (err) {
|
||||
const result = context.genFunc() catch |err| switch (err) {
|
||||
error.CodegenFail => {
|
||||
decl.analysis = .codegen_failure;
|
||||
try module.failed_decls.put(module.gpa, decl, context.err_msg);
|
||||
|
||||
Reference in New Issue
Block a user