authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-22 21:17:49+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-27 21:34:51+01:00
log9b5d61430fc7297b24d870adf42392ad113fa21b
treea74c75edec9e07003a6218897bc1971b05a12e66
parent90d8544d4066a8aa42f1fe1d8cd052e8099d8152
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Implement slices


1 files changed, 102 insertions(+), 11 deletions(-)

src/arch/wasm/CodeGen.zig+102-11
...@@ -1015,6 +1015,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1015,6 +1015,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1015 .loop => self.airLoop(inst),1015 .loop => self.airLoop(inst),
1016 .not => self.airNot(inst),1016 .not => self.airNot(inst),
1017 .ret => self.airRet(inst),1017 .ret => self.airRet(inst),
1018 .slice_len => self.airSliceLen(inst),
1019 .slice_elem_val => self.airSliceElemVal(inst),
1018 .store => self.airStore(inst),1020 .store => self.airStore(inst),
1019 .struct_field_ptr => self.airStructFieldPtr(inst),1021 .struct_field_ptr => self.airStructFieldPtr(inst),
1020 .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0),1022 .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0),
...@@ -1145,13 +1147,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1145,13 +1147,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1145 // in memory1147 // in memory
1146 try self.emitWValue(rhs);1148 try self.emitWValue(rhs);
1147 const tag_local = try self.allocLocal(tag_ty);1149 const tag_local = try self.allocLocal(tag_ty);
1148 const payload_local = try self.allocLocal(payload_ty);
11491150
1150 try self.addLabel(.local_set, payload_local.local);1151 if (payload_ty.hasCodeGenBits()) {
1152 const payload_local = try self.allocLocal(payload_ty);
1153 try self.addLabel(.local_set, payload_local.local);
1154 try self.store(lhs, payload_local, payload_ty, payload_offset);
1155 }
1151 try self.addLabel(.local_set, tag_local.local);1156 try self.addLabel(.local_set, tag_local.local);
11521157
1153 try self.store(lhs, tag_local, tag_ty, 0);1158 try self.store(lhs, tag_local, tag_ty, 0);
1154 return try self.store(lhs, payload_local, payload_ty, payload_offset);1159 return;
1155 },1160 },
1156 .local => {1161 .local => {
1157 // Load values from `rhs` stack position and store in `lhs` instead1162 // Load values from `rhs` stack position and store in `lhs` instead
...@@ -1197,9 +1202,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1197,9 +1202,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1197 try self.emitWValue(lhs);1202 try self.emitWValue(lhs);
1198 try self.emitWValue(rhs);1203 try self.emitWValue(rhs);
1199 const valtype = try self.typeToValtype(ty);1204 const valtype = try self.typeToValtype(ty);
1205 // check if we should pass by pointer or value based on ABI size
1206 // TODO: Implement a way to get ABI values from a given type,
1207 // that is portable across the backend, rather than copying logic.
1208 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)
1209 @intCast(u8, ty.abiSize(self.target))
1210 else
1211 @as(u8, 4);
1200 const opcode = buildOpcode(.{1212 const opcode = buildOpcode(.{
1201 .valtype1 = valtype,1213 .valtype1 = valtype,
1202 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size1214 .width = abi_size * 8, // use bitsize instead of byte size
1203 .op = .store,1215 .op = .store,
1204 });1216 });
12051217
...@@ -1220,7 +1232,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1220,7 +1232,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1220 const ty = self.air.getRefType(ty_op.ty);1232 const ty = self.air.getRefType(ty_op.ty);
12211233
1222 return switch (ty.zigTypeTag()) {1234 return switch (ty.zigTypeTag()) {
1223 .Struct, .ErrorUnion, .Optional => operand, // pass as pointer1235 .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer
1224 else => switch (operand) {1236 else => switch (operand) {
1225 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),1237 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),
1226 else => try self.load(operand, ty, 0),1238 else => try self.load(operand, ty, 0),
...@@ -1233,9 +1245,17 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -1233,9 +1245,17 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1233 try self.emitWValue(operand);1245 try self.emitWValue(operand);
1234 // Build the opcode with the right bitsize1246 // Build the opcode with the right bitsize
1235 const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed;1247 const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed;
1248 // check if we should pass by pointer or value based on ABI size
1249 // TODO: Implement a way to get ABI values from a given type,
1250 // that is portable across the backend, rather than copying logic.
1251 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)
1252 @intCast(u8, ty.abiSize(self.target))
1253 else
1254 @as(u8, 4);
1255
1236 const opcode = buildOpcode(.{1256 const opcode = buildOpcode(.{
1237 .valtype1 = try self.typeToValtype(ty),1257 .valtype1 = try self.typeToValtype(ty),
1238 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size1258 .width = abi_size * 8, // use bitsize instead of byte size
1239 .op = .load,1259 .op = .load,
1240 .signedness = signedness,1260 .signedness = signedness,
1241 });1261 });
...@@ -1399,14 +1419,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1399,14 +1419,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1399 const payload_val = pl.data;1419 const payload_val = pl.data;
1400 // no error, so write a '0' const1420 // no error, so write a '0' const
1401 try self.addImm32(0);1421 try self.addImm32(0);
1402 // after the error code, we emit the payload1422
1403 try self.emitConstant(payload_val, payload_type);1423 if (payload_type.hasCodeGenBits()) {
1424 // after the error code, we emit the payload
1425 try self.emitConstant(payload_val, payload_type);
1426 }
1404 } else {1427 } else {
1405 // write the error val1428 // write the error val
1406 try self.emitConstant(val, error_type);1429 try self.emitConstant(val, error_type);
14071430
1408 // no payload, so write a '0' const1431 if (payload_type.hasCodeGenBits()) {
1409 try self.addImm32(0);1432 // no payload, so write a '0' const
1433 try self.addImm32(0);
1434 }
1410 }1435 }
1411 },1436 },
1412 .Optional => {1437 .Optional => {
...@@ -1867,8 +1892,10 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -1867,8 +1892,10 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
1867 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1892 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1868 const operand = self.resolveInst(ty_op.operand);1893 const operand = self.resolveInst(ty_op.operand);
1869 const err_ty = self.air.typeOf(ty_op.operand);1894 const err_ty = self.air.typeOf(ty_op.operand);
1895 const payload_ty = err_ty.errorUnionPayload();
1896 if (!payload_ty.hasCodeGenBits()) return WValue.none;
1870 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));1897 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
1871 return self.load(operand, err_ty.errorUnionPayload(), offset);1898 return try self.load(operand, payload_ty, offset);
1872}1899}
18731900
1874fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1901fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1961,3 +1988,67 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1961,3 +1988,67 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1961 .offset = @intCast(u32, offset),1988 .offset = @intCast(u32, offset),
1962 } };1989 } };
1963}1990}
1991
1992fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1993 if (self.liveness.isUnused(inst)) return WValue.none;
1994
1995 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1996 const operand = self.resolveInst(ty_op.operand);
1997 const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8;
1998
1999 // Get pointer to slice
2000 try self.emitWValue(operand);
2001 // length of slice is stored after the pointer of the slice
2002 const extra_index = try self.addExtra(Mir.MemArg{
2003 .offset = pointer_width,
2004 .alignment = pointer_width,
2005 });
2006 try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });
2007
2008 const result = try self.allocLocal(Type.initTag(.i32)); // pointer is always i32
2009 // store slice length in local
2010 try self.addLabel(.local_set, result.local);
2011 return result;
2012}
2013
2014fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2015 if (self.liveness.isUnused(inst)) return WValue.none;
2016
2017 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2018 const slice_ty = self.air.typeOf(bin_op.lhs);
2019 const slice = self.resolveInst(bin_op.lhs);
2020 const index = self.resolveInst(bin_op.rhs);
2021 const elem_ty = slice_ty.childType();
2022 const elem_size = elem_ty.abiSize(self.target);
2023
2024 // load pointer onto stack
2025 try self.emitWValue(slice);
2026
2027 // calculate index into slice
2028 try self.emitWValue(index);
2029 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
2030 try self.addTag(.i32_mul);
2031 try self.addTag(.i32_add);
2032
2033 const abi_size = if (elem_size < 8)
2034 @intCast(u8, elem_size)
2035 else
2036 @as(u8, 4); // elements larger than 8 bytes will be passed by pointer
2037
2038 const extra_index = try self.addExtra(Mir.MemArg{
2039 .offset = 0,
2040 .alignment = elem_ty.abiAlignment(self.target),
2041 });
2042 const signedness: std.builtin.Signedness = if (elem_ty.isUnsignedInt()) .unsigned else .signed;
2043 const opcode = buildOpcode(.{
2044 .valtype1 = try self.typeToValtype(elem_ty),
2045 .width = abi_size * 8,
2046 .op = .load,
2047 .signedness = signedness,
2048 });
2049 try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = extra_index } });
2050
2051 const result = try self.allocLocal(elem_ty);
2052 try self.addLabel(.local_set, result.local);
2053 return result;
2054}