| ... | @@ -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 memory | 1147 | // 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); | | |
| 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 | try self.addLabel(.local_set, tag_local.local); | 1156 | try self.addLabel(.local_set, tag_local.local); |
| 1152 | | 1157 | |
| 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` instead | 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,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 size | 1214 | .width = abi_size * 8, // use bitsize instead of byte size |
| 1203 | .op = .store, | 1215 | .op = .store, |
| 1204 | }); | 1216 | }); |
| 1205 | | 1217 | |
| ... | @@ -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); |
| 1221 | | 1233 | |
| 1222 | return switch (ty.zigTypeTag()) { | 1234 | return switch (ty.zigTypeTag()) { |
| 1223 | .Struct, .ErrorUnion, .Optional => operand, // pass as pointer | 1235 | .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 bitsize | 1246 | // 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 size | 1258 | .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' const | 1420 | // 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 payload | 1422 | |
| 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 val | 1428 | // write the error val |
| 1406 | try self.emitConstant(val, error_type); | 1429 | try self.emitConstant(val, error_type); |
| 1407 | | 1430 | |
| 1408 | // no payload, so write a '0' const | 1431 | 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 | } |
| 1873 | | 1900 | |
| 1874 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,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 | |
| | 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 | } |