| ... | ... | @@ -1015,6 +1015,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1015 | 1015 | .loop => self.airLoop(inst), |
| 1016 | 1016 | .not => self.airNot(inst), |
| 1017 | 1017 | .ret => self.airRet(inst), |
| 1018 | .slice_len => self.airSliceLen(inst), |
| 1019 | .slice_elem_val => self.airSliceElemVal(inst), |
| 1018 | 1020 | .store => self.airStore(inst), |
| 1019 | 1021 | .struct_field_ptr => self.airStructFieldPtr(inst), |
| 1020 | 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 | 1147 | // in memory |
| 1146 | 1148 | try self.emitWValue(rhs); |
| 1147 | 1149 | const tag_local = try self.allocLocal(tag_ty); |
| 1148 | | const payload_local = try self.allocLocal(payload_ty); |
| 1149 | 1150 | |
| 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 | 1156 | try self.addLabel(.local_set, tag_local.local); |
| 1152 | 1157 | |
| 1153 | 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 | 1161 | .local => { |
| 1157 | 1162 | // 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 | 1202 | try self.emitWValue(lhs); |
| 1198 | 1203 | try self.emitWValue(rhs); |
| 1199 | 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 | 1212 | const opcode = buildOpcode(.{ |
| 1201 | 1213 | .valtype1 = valtype, |
| 1202 | | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| 1214 | .width = abi_size * 8, // use bitsize instead of byte size |
| 1203 | 1215 | .op = .store, |
| 1204 | 1216 | }); |
| 1205 | 1217 | |
| ... | ... | @@ -1220,7 +1232,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1220 | 1232 | const ty = self.air.getRefType(ty_op.ty); |
| 1221 | 1233 | |
| 1222 | 1234 | return switch (ty.zigTypeTag()) { |
| 1223 | | .Struct, .ErrorUnion, .Optional => operand, // pass as pointer |
| 1235 | .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer |
| 1224 | 1236 | else => switch (operand) { |
| 1225 | 1237 | .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset), |
| 1226 | 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 | 1245 | try self.emitWValue(operand); |
| 1234 | 1246 | // Build the opcode with the right bitsize |
| 1235 | 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 | 1256 | const opcode = buildOpcode(.{ |
| 1237 | 1257 | .valtype1 = try self.typeToValtype(ty), |
| 1238 | | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| 1258 | .width = abi_size * 8, // use bitsize instead of byte size |
| 1239 | 1259 | .op = .load, |
| 1240 | 1260 | .signedness = signedness, |
| 1241 | 1261 | }); |
| ... | ... | @@ -1399,14 +1419,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1399 | 1419 | const payload_val = pl.data; |
| 1400 | 1420 | // no error, so write a '0' const |
| 1401 | 1421 | try self.addImm32(0); |
| 1402 | | // after the error code, we emit the payload |
| 1403 | | try self.emitConstant(payload_val, payload_type); |
| 1422 | |
| 1423 | if (payload_type.hasCodeGenBits()) { |
| 1424 | // after the error code, we emit the payload |
| 1425 | try self.emitConstant(payload_val, payload_type); |
| 1426 | } |
| 1404 | 1427 | } else { |
| 1405 | 1428 | // write the error val |
| 1406 | 1429 | try self.emitConstant(val, error_type); |
| 1407 | 1430 | |
| 1408 | | // no payload, so write a '0' const |
| 1409 | | try self.addImm32(0); |
| 1431 | if (payload_type.hasCodeGenBits()) { |
| 1432 | // no payload, so write a '0' const |
| 1433 | try self.addImm32(0); |
| 1434 | } |
| 1410 | 1435 | } |
| 1411 | 1436 | }, |
| 1412 | 1437 | .Optional => { |
| ... | ... | @@ -1867,8 +1892,10 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 1867 | 1892 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1868 | 1893 | const operand = self.resolveInst(ty_op.operand); |
| 1869 | 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 | 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 | } |
| 1873 | 1900 | |
| 1874 | 1901 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1961,3 +1988,67 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1961 | 1988 | .offset = @intCast(u32, offset), |
| 1962 | 1989 | } }; |
| 1963 | 1990 | } |
| 1991 | |
| 1992 | fn 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 | |
| 2014 | fn 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 | } |