authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-14 19:23:31+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-18 07:43:33+02:00
log502f5d824626675aecf9daaaf6af7548ef3e7f09
tree9c51f320b38c551a6d72f13be5e326e1c3dea34c
parent03a3ea2c1577208311d85482faadac6361442971

wasm: Fix C-ABI for 128 bit integers

We now pass the correct wasm type when the return type is a 128-bit integer. When a function accepts a 128-bit integer, we now allocate space on the virtual stack and store both arguments within that space as currently all following instructions assume the 128 bit integer doesn't live in a local, but the stack.

1 files changed, 46 insertions(+), 60 deletions(-)

src/arch/wasm/CodeGen.zig+46-60
...@@ -794,7 +794,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std....@@ -794,7 +794,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.
794 defer returns.deinit();794 defer returns.deinit();
795795
796 if (firstParamSRet(fn_info, target)) {796 if (firstParamSRet(fn_info, target)) {
797 try params.append(typeToValtype(fn_info.return_type, target));797 try params.append(.i32); // memory address is always a 32-bit handle
798 } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) {798 } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) {
799 if (fn_info.cc == .C) {799 if (fn_info.cc == .C) {
800 const res_classes = abi.classifyType(fn_info.return_type, target);800 const res_classes = abi.classifyType(fn_info.return_type, target);
...@@ -824,7 +824,10 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std....@@ -824,7 +824,10 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.
824 }824 }
825 }825 }
826 },826 },
827 else => try params.append(typeToValtype(param_type, target)),827 else => if (isByRef(param_type, target))
828 try params.append(.i32)
829 else
830 try params.append(typeToValtype(param_type, target)),
828 }831 }
829 }832 }
830 }833 }
...@@ -844,7 +847,6 @@ pub fn generate(...@@ -844,7 +847,6 @@ pub fn generate(
844 code: *std.ArrayList(u8),847 code: *std.ArrayList(u8),
845 debug_output: codegen.DebugInfoOutput,848 debug_output: codegen.DebugInfoOutput,
846) codegen.GenerateSymbolError!codegen.FnResult {849) codegen.GenerateSymbolError!codegen.FnResult {
847 _ = debug_output; // TODO
848 _ = src_loc;850 _ = src_loc;
849 var code_gen: Self = .{851 var code_gen: Self = .{
850 .gpa = bin_file.allocator,852 .gpa = bin_file.allocator,
...@@ -1088,9 +1090,9 @@ fn lowerArg(self: *Self, cc: std.builtin.CallingConvention, ty: Type, value: WVa...@@ -1088,9 +1090,9 @@ fn lowerArg(self: *Self, cc: std.builtin.CallingConvention, ty: Type, value: WVa
1088 assert(ty.abiSize(self.target) == 16);1090 assert(ty.abiSize(self.target) == 16);
1089 // in this case we have an integer or float that must be lowered as 2 i64's.1091 // in this case we have an integer or float that must be lowered as 2 i64's.
1090 try self.emitWValue(value);1092 try self.emitWValue(value);
1091 try self.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 16 });1093 try self.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1092 try self.emitWValue(value);1094 try self.emitWValue(value);
1093 try self.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 16 });1095 try self.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
1094 },1096 },
1095 else => return self.lowerToStack(value),1097 else => return self.lowerToStack(value),
1096 }1098 }
...@@ -1221,14 +1223,8 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {...@@ -1221,14 +1223,8 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
1221 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.1223 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
1222 // If not, we lower it ourselves manually1224 // If not, we lower it ourselves manually
1223 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {1225 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {
1224 switch (dst) {1226 try self.lowerToStack(dst);
1225 .stack_offset => try self.emitWValue(try self.buildPointerOffset(dst, 0, .new)),1227 try self.lowerToStack(src);
1226 else => try self.emitWValue(dst),
1227 }
1228 switch (src) {
1229 .stack_offset => try self.emitWValue(try self.buildPointerOffset(src, 0, .new)),
1230 else => try self.emitWValue(src),
1231 }
1232 try self.emitWValue(len);1228 try self.emitWValue(len);
1233 try self.addExtended(.memory_copy);1229 try self.addExtended(.memory_copy);
1234 return;1230 return;
...@@ -1792,7 +1788,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1792,7 +1788,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1792 const ty = self.air.typeOf(bin_op.lhs).childType();1788 const ty = self.air.typeOf(bin_op.lhs).childType();
17931789
1794 try self.store(lhs, rhs, ty, 0);1790 try self.store(lhs, rhs, ty, 0);
1795 return .none;1791 return WValue{ .none = {} };
1796}1792}
17971793
1798fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {1794fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
...@@ -1848,11 +1844,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1848,11 +1844,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1848 try self.emitWValue(lhs);1844 try self.emitWValue(lhs);
1849 // In this case we're actually interested in storing the stack position1845 // In this case we're actually interested in storing the stack position
1850 // into lhs, so we calculate that and emit that instead1846 // into lhs, so we calculate that and emit that instead
1851 if (rhs == .stack_offset) {1847 try self.lowerToStack(rhs);
1852 try self.emitWValue(try self.buildPointerOffset(rhs, 0, .new));1848
1853 } else {
1854 try self.emitWValue(rhs);
1855 }
1856 const valtype = typeToValtype(ty, self.target);1849 const valtype = typeToValtype(ty, self.target);
1857 const abi_size = @intCast(u8, ty.abiSize(self.target));1850 const abi_size = @intCast(u8, ty.abiSize(self.target));
18581851
...@@ -1912,14 +1905,29 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1912,14 +1905,29 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1912 const arg_index = self.arg_index;1905 const arg_index = self.arg_index;
1913 const arg = self.args[arg_index];1906 const arg = self.args[arg_index];
1914 const cc = self.decl.ty.fnInfo().cc;1907 const cc = self.decl.ty.fnInfo().cc;
1908 const arg_ty = self.air.typeOfIndex(inst);
1915 if (cc == .C) {1909 if (cc == .C) {
1916 const ty = self.air.typeOfIndex(inst);1910 const arg_classes = abi.classifyType(arg_ty, self.target);
1917 const arg_classes = abi.classifyType(ty, self.target);
1918 for (arg_classes) |class| {1911 for (arg_classes) |class| {
1919 if (class != .none) {1912 if (class != .none) {
1920 self.arg_index += 1;1913 self.arg_index += 1;
1921 }1914 }
1922 }1915 }
1916
1917 // When we have an argument that's passed using more than a single parameter,
1918 // we combine them into a single stack value
1919 if (arg_classes[0] == .direct and arg_classes[1] == .direct) {
1920 if (arg_ty.zigTypeTag() != .Int) {
1921 return self.fail(
1922 "TODO: Implement C-ABI argument for type '{}'",
1923 .{arg_ty.fmt(self.bin_file.base.options.module.?)},
1924 );
1925 }
1926 const result = try self.allocStack(arg_ty);
1927 try self.store(result, arg, Type.u64, 0);
1928 try self.store(result, self.args[arg_index + 1], Type.u64, 8);
1929 return result;
1930 }
1923 } else {1931 } else {
1924 self.arg_index += 1;1932 self.arg_index += 1;
1925 }1933 }
...@@ -1943,7 +1951,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1943,7 +1951,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1943 std.dwarf.OP.WASM_local,1951 std.dwarf.OP.WASM_local,
1944 });1952 });
1945 leb.writeULEB128(dbg_info.writer(), arg.local) catch unreachable;1953 leb.writeULEB128(dbg_info.writer(), arg.local) catch unreachable;
1946 try self.addDbgInfoTypeReloc(self.air.typeOfIndex(inst));1954 try self.addDbgInfoTypeReloc(arg_ty);
1947 dbg_info.appendSliceAssumeCapacity(name);1955 dbg_info.appendSliceAssumeCapacity(name);
1948 dbg_info.appendAssumeCapacity(0);1956 dbg_info.appendAssumeCapacity(0);
1949 },1957 },
...@@ -2503,14 +2511,8 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper...@@ -2503,14 +2511,8 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper
25032511
2504 // ensure that when we compare pointers, we emit2512 // ensure that when we compare pointers, we emit
2505 // the true pointer of a stack value, rather than the stack pointer.2513 // the true pointer of a stack value, rather than the stack pointer.
2506 switch (lhs) {2514 try self.lowerToStack(lhs);
2507 .stack_offset => try self.emitWValue(try self.buildPointerOffset(lhs, 0, .new)),2515 try self.lowerToStack(rhs);
2508 else => try self.emitWValue(lhs),
2509 }
2510 switch (rhs) {
2511 .stack_offset => try self.emitWValue(try self.buildPointerOffset(rhs, 0, .new)),
2512 else => try self.emitWValue(rhs),
2513 }
25142516
2515 const signedness: std.builtin.Signedness = blk: {2517 const signedness: std.builtin.Signedness = blk: {
2516 // by default we tell the operand type is unsigned (i.e. bools and enum values)2518 // by default we tell the operand type is unsigned (i.e. bools and enum values)
...@@ -2560,11 +2562,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2560,11 +2562,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2560 // if operand has codegen bits we should break with a value2562 // if operand has codegen bits we should break with a value
2561 if (self.air.typeOf(br.operand).hasRuntimeBitsIgnoreComptime()) {2563 if (self.air.typeOf(br.operand).hasRuntimeBitsIgnoreComptime()) {
2562 const operand = try self.resolveInst(br.operand);2564 const operand = try self.resolveInst(br.operand);
2563 const op = switch (operand) {2565 try self.lowerToStack(operand);
2564 .stack_offset => try self.buildPointerOffset(operand, 0, .new),
2565 else => operand,
2566 };
2567 try self.emitWValue(op);
25682566
2569 if (block.value != .none) {2567 if (block.value != .none) {
2570 try self.addLabel(.local_set, block.value.local);2568 try self.addLabel(.local_set, block.value.local);
...@@ -3295,11 +3293,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3295,11 +3293,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3295 const ptr_local = try self.load(ptr, Type.usize, 0);3293 const ptr_local = try self.load(ptr, Type.usize, 0);
3296 try self.addLabel(.local_get, ptr_local.local);3294 try self.addLabel(.local_get, ptr_local.local);
3297 } else {3295 } else {
3298 const pointer = switch (ptr) {3296 try self.lowerToStack(ptr);
3299 .stack_offset => try self.buildPointerOffset(ptr, 0, .new),
3300 else => ptr,
3301 };
3302 try self.emitWValue(pointer);
3303 }3297 }
33043298
3305 // calculate index into slice3299 // calculate index into slice
...@@ -3332,11 +3326,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3332,11 +3326,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3332 const ptr_local = try self.load(ptr, Type.usize, 0);3326 const ptr_local = try self.load(ptr, Type.usize, 0);
3333 try self.addLabel(.local_get, ptr_local.local);3327 try self.addLabel(.local_get, ptr_local.local);
3334 } else {3328 } else {
3335 const pointer = switch (ptr) {3329 try self.lowerToStack(ptr);
3336 .stack_offset => try self.buildPointerOffset(ptr, 0, .new),
3337 else => ptr,
3338 };
3339 try self.emitWValue(pointer);
3340 }3330 }
33413331
3342 // calculate index into ptr3332 // calculate index into ptr
...@@ -3365,11 +3355,7 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -3365,11 +3355,7 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
3365 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });3355 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
3366 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });3356 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
33673357
3368 const pointer = switch (ptr) {3358 try self.lowerToStack(ptr);
3369 .stack_offset => try self.buildPointerOffset(ptr, 0, .new),
3370 else => ptr,
3371 };
3372 try self.emitWValue(pointer);
3373 try self.emitWValue(offset);3359 try self.emitWValue(offset);
3374 try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target))));3360 try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target))));
3375 try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));3361 try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));
...@@ -3400,10 +3386,7 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void...@@ -3400,10 +3386,7 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
3400 // When bulk_memory is enabled, we lower it to wasm's memset instruction.3386 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
3401 // If not, we lower it ourselves3387 // If not, we lower it ourselves
3402 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {3388 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {
3403 switch (ptr) {3389 try self.lowerToStack(ptr);
3404 .stack_offset => try self.emitWValue(try self.buildPointerOffset(ptr, 0, .new)),
3405 else => try self.emitWValue(ptr),
3406 }
3407 try self.emitWValue(value);3390 try self.emitWValue(value);
3408 try self.emitWValue(len);3391 try self.emitWValue(len);
3409 try self.addExtended(.memory_fill);3392 try self.addExtended(.memory_fill);
...@@ -3490,12 +3473,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3490,12 +3473,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3490 const elem_ty = array_ty.childType();3473 const elem_ty = array_ty.childType();
3491 const elem_size = elem_ty.abiSize(self.target);3474 const elem_size = elem_ty.abiSize(self.target);
34923475
3493 const array_ptr = switch (array) {3476 try self.lowerToStack(array);
3494 .stack_offset => try self.buildPointerOffset(array, 0, .new),
3495 else => array,
3496 };
3497
3498 try self.emitWValue(array_ptr);
3499 try self.emitWValue(index);3477 try self.emitWValue(index);
3500 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));3478 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3501 try self.addTag(.i32_mul);3479 try self.addTag(.i32_mul);
...@@ -3518,6 +3496,10 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3518,6 +3496,10 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3518 const dest_ty = self.air.typeOfIndex(inst);3496 const dest_ty = self.air.typeOfIndex(inst);
3519 const op_ty = self.air.typeOf(ty_op.operand);3497 const op_ty = self.air.typeOf(ty_op.operand);
35203498
3499 if (op_ty.abiSize(self.target) > 8) {
3500 return self.fail("TODO: floatToInt for integers/floats with bitsize larger than 64 bits", .{});
3501 }
3502
3521 try self.emitWValue(operand);3503 try self.emitWValue(operand);
3522 const op = buildOpcode(.{3504 const op = buildOpcode(.{
3523 .op = .trunc,3505 .op = .trunc,
...@@ -3541,6 +3523,10 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3541,6 +3523,10 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3541 const dest_ty = self.air.typeOfIndex(inst);3523 const dest_ty = self.air.typeOfIndex(inst);
3542 const op_ty = self.air.typeOf(ty_op.operand);3524 const op_ty = self.air.typeOf(ty_op.operand);
35433525
3526 if (op_ty.abiSize(self.target) > 8) {
3527 return self.fail("TODO: intToFloat for integers/floats with bitsize larger than 64 bits", .{});
3528 }
3529
3544 try self.emitWValue(operand);3530 try self.emitWValue(operand);
3545 const op = buildOpcode(.{3531 const op = buildOpcode(.{
3546 .op = .convert,3532 .op = .convert,