diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 171 |
1 files changed, 118 insertions, 53 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 6d25393c4f..831f7be34a 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -187,7 +187,13 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { 32 => switch (args.valtype1.?) { .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, .i32 => return .i32_load, - .f32, .f64 => unreachable, + .f32 => return .f32_load, + .f64 => unreachable, + }, + 64 => switch (args.valtype1.?) { + .i64 => return .i64_load, + .f64 => return .f64_load, + else => unreachable, }, else => unreachable, } else switch (args.valtype1.?) { @@ -216,6 +222,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { }, 64 => switch (args.valtype1.?) { .i64 => return .i64_store, + .f64 => return .f64_store, else => unreachable, }, else => unreachable, @@ -505,7 +512,10 @@ gpa: *mem.Allocator, /// 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) = .{}, +blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { + label: u32, + value: WValue, +}) = .{}, /// `bytes` contains the wasm bytecode belonging to the 'code' section. code: ArrayList(u8), /// Contains the generated function type bytecode for the current function @@ -984,8 +994,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .dbg_stmt => WValue.none, .intcast => self.airIntcast(inst), - .is_err => self.airIsErr(inst, .i32_eq), - .is_non_err => self.airIsErr(inst, .i32_ne), + .is_err => self.airIsErr(inst, .i32_ne), + .is_non_err => self.airIsErr(inst, .i32_eq), .is_null => self.airIsNull(inst, .i32_ne), .is_non_null => self.airIsNull(inst, .i32_eq), @@ -1065,12 +1075,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ret_ty = target.ty.fnReturnType(); switch (ret_ty.zigTypeTag()) { - .ErrorUnion, .Optional => { + .Void, .NoReturn => return WValue.none, + else => { const result_local = try self.allocLocal(ret_ty); try self.addLabel(.local_set, result_local.local); return result_local; }, - else => return WValue.none, } } @@ -1086,7 +1096,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (abi_size == 0) return WValue{ .none = {} }; // local, containing the offset to the stack position - const local = try self.allocLocal(child_type); + const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 try self.moveStack(@intCast(u32, abi_size), local.local); return local; @@ -1114,29 +1124,64 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro var buf: Type.Payload.ElemType = undefined; const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf); const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8); - const payload_offset = @intCast(u32, tag_ty.abiSize(self.target)); - - if (rhs == .constant) { - // constant will contain both tag and payload, - // so save those in 2 temporary locals before storing them - // in memory - try self.emitWValue(rhs); - const tag_local = try self.allocLocal(tag_ty); - const payload_local = try self.allocLocal(payload_ty); - - try self.addLabel(.local_set, payload_local.local); - try self.addLabel(.local_set, tag_local.local); - - try self.store(lhs, tag_local, tag_ty, 0); - return try self.store(lhs, payload_local, payload_ty, payload_offset); - } else { - // Load values from `rhs` stack position and store in `lhs` instead - const tag_local = try self.load(rhs, tag_ty, 0); - const payload_local = try self.load(rhs, payload_ty, payload_offset); + const payload_offset = if (ty.zigTypeTag() == .ErrorUnion) + @intCast(u32, tag_ty.abiSize(self.target)) + else + @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target)); + + switch (rhs) { + .constant => { + // constant will contain both tag and payload, + // so save those in 2 temporary locals before storing them + // in memory + try self.emitWValue(rhs); + const tag_local = try self.allocLocal(tag_ty); + const payload_local = try self.allocLocal(payload_ty); + + try self.addLabel(.local_set, payload_local.local); + try self.addLabel(.local_set, tag_local.local); + + try self.store(lhs, tag_local, tag_ty, 0); + return try self.store(lhs, payload_local, payload_ty, payload_offset); + }, + .local => { + // Load values from `rhs` stack position and store in `lhs` instead + const tag_local = try self.load(rhs, tag_ty, 0); + const payload_local = try self.load(rhs, payload_ty, payload_offset); - try self.store(lhs, tag_local, tag_ty, 0); - return try self.store(lhs, payload_local, payload_ty, payload_offset); + try self.store(lhs, tag_local, tag_ty, 0); + return try self.store(lhs, payload_local, payload_ty, payload_offset); + }, + .local_with_offset => |with_offset| { + const tag_local = try self.allocLocal(tag_ty); + try self.addImm32(0); + try self.store(lhs, tag_local, tag_ty, 0); + + return try self.store( + lhs, + .{ .local = with_offset.local }, + payload_ty, + with_offset.offset, + ); + }, + else => unreachable, + } + }, + .Struct => { + // we are copying a struct with its fields. + // Replace this with a wasm memcpy instruction once we support that feature. + const fields_len = ty.structFieldCount(); + var index: usize = 0; + while (index < fields_len) : (index += 1) { + const field_ty = ty.structFieldType(index); + if (!field_ty.hasCodeGenBits()) continue; + const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch { + return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); + }; + const field_local = try self.load(rhs, field_ty, field_offset); + try self.store(lhs, field_local, field_ty, field_offset); } + return; }, else => {}, } @@ -1429,21 +1474,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue { 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); + // if block_ty is non-empty, we create a register to store the temporary value + const block_result: WValue = if (block_ty != wasm.block_empty) + try self.allocLocal(self.air.getRefType(ty_pl.ty)) + else + WValue.none; + + try self.startBlock(.block, wasm.block_empty); // Here we set the current block idx, so breaks know the depth to jump // to when breaking out. - try self.blocks.putNoClobber(self.gpa, inst, self.block_depth); + try self.blocks.putNoClobber(self.gpa, inst, .{ + .label = self.block_depth, + .value = block_result, + }); try self.genBody(body); try self.endBlock(); - return .none; + return block_result; } /// appends a new wasm block to the code section and increases the `block_depth` by 1 -fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8, with_offset: ?usize) !void { +fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8) !void { self.block_depth += 1; - const offset = with_offset orelse self.mir_instructions.len; - try self.addInstAt(offset, .{ + try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(block_tag), .data = .{ .block_type = valtype }, }); @@ -1462,7 +1515,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // 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.startBlock(.loop, wasm.block_empty); try self.genBody(body); // breaking to the index of a loop block will continue the loop instead @@ -1480,13 +1533,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; // TODO: Handle death instructions for then and else body - // insert blocks at the position of `offset` so - // the condition can jump to it - const offset = self.mir_instructions.len; - try self.emitWValue(condition); - // result type is always noreturn, so use `block_empty` as type. - try self.startBlock(.block, wasm.block_empty, offset); + try self.startBlock(.block, wasm.block_empty); + // emit the conditional value + try self.emitWValue(condition); // we inserted the block in front of the condition // so now check if condition matches. If not, break outside this block @@ -1539,15 +1589,20 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const br = self.air.instructions.items(.data)[inst].br; + const block = self.blocks.get(br.block_inst).?; // if operand has codegen bits we should break with a value if (self.air.typeOf(br.operand).hasCodeGenBits()) { try self.emitWValue(self.resolveInst(br.operand)); + + if (block.value != .none) { + try self.addLabel(.local_set, block.value.local); + } } // We map every block to its block index. // We then determine how far we have to jump to it by subtracting it from current block depth - const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?; + const idx: u32 = self.block_depth - block.label; try self.addLabel(.br, idx); return .none; @@ -1677,7 +1732,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } case_list.appendAssumeCapacity(.{ .values = values, .body = case_body }); - try self.startBlock(.block, blocktype, null); + try self.startBlock(.block, blocktype); } // When the highest and lowest values are seperated by '50', @@ -1690,7 +1745,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; const has_else_body = else_body.len != 0; if (has_else_body) { - try self.startBlock(.block, blocktype, null); + try self.startBlock(.block, blocktype); } if (!is_sparse) { @@ -1698,7 +1753,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // The value 'target' represents the index into the table. // Each index in the table represents a label to the branch // to jump to. - try self.startBlock(.block, blocktype, null); + try self.startBlock(.block, blocktype); try self.emitWValue(target); if (lowest < 0) { // since br_table works using indexes, starting from '0', we must ensure all values @@ -1754,7 +1809,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.br_if, 0); } else { // in multi-value prongs we must check if any prongs match the target value. - try self.startBlock(.block, blocktype, null); + try self.startBlock(.block, blocktype); for (case.values) |value| { try self.emitWValue(target); try self.emitConstant(value.value, target_ty); @@ -1794,7 +1849,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W .alignment = err_ty.abiAlignment(self.target), }); try self.addInst(.{ - .tag = .i32_load, + .tag = .i32_load16_u, .data = .{ .payload = mem_arg_index }, }); @@ -1811,14 +1866,14 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = self.resolveInst(ty_op.operand); const err_ty = self.air.typeOf(ty_op.operand); - const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target) / 8); - - return self.load(operand, self.air.getRefType(ty_op.ty), offset); + const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target)); + return self.load(operand, err_ty.errorUnionPayload(), offset); } fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - return self.resolveInst(ty_op.operand); + _ = ty_op; + return self.fail("TODO: wasm airWrapErrUnionPayload", .{}); } fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1880,8 +1935,9 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { var buf: Type.Payload.ElemType = undefined; const child_ty = opt_ty.optionalChild(&buf); + const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target); - return self.load(operand, child_ty, @as(u32, 1)); // null tag is 1 byte + return self.load(operand, child_ty, @intCast(u32, offset)); } fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1893,5 +1949,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - return self.resolveInst(ty_op.operand); + const operand = self.resolveInst(ty_op.operand); + + const op_ty = self.air.typeOf(ty_op.operand); + const optional_ty = self.air.getRefType(ty_op.ty); + const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target); + + return WValue{ .local_with_offset = .{ + .local = operand.local, + .offset = @intCast(u32, offset), + } }; } |
