| ... | @@ -1261,27 +1261,84 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1261,27 +1261,84 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1261 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1261 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1262 | } | 1262 | } |
| 1263 | | 1263 | |
| | 1264 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| | 1265 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 1266 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 1267 | const optional_ty = self.air.typeOfIndex(inst); |
| | 1268 | const abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); |
| | 1269 | |
| | 1270 | // Optional with a zero-bit payload type is just a boolean true |
| | 1271 | if (abi_size == 1) { |
| | 1272 | break :result MCValue{ .immediate = 1 }; |
| | 1273 | } else { |
| | 1274 | return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); |
| | 1275 | } |
| | 1276 | }; |
| | 1277 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| | 1278 | } |
| | 1279 | |
| | 1280 | /// Given an error union, returns the error |
| | 1281 | fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { |
| | 1282 | const payload_ty = error_union_ty.errorUnionPayload(); |
| | 1283 | if (!payload_ty.hasRuntimeBits()) return error_union_mcv; |
| | 1284 | |
| | 1285 | switch (error_union_mcv) { |
| | 1286 | .register => return self.fail("TODO errUnionErr for registers", .{}), |
| | 1287 | .stack_argument_offset => |off| { |
| | 1288 | return MCValue{ .stack_argument_offset = off }; |
| | 1289 | }, |
| | 1290 | .stack_offset => |off| { |
| | 1291 | return MCValue{ .stack_offset = off }; |
| | 1292 | }, |
| | 1293 | .memory => |addr| { |
| | 1294 | return MCValue{ .memory = addr }; |
| | 1295 | }, |
| | 1296 | else => unreachable, // invalid MCValue for an error union |
| | 1297 | } |
| | 1298 | } |
| | 1299 | |
| 1264 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { | 1300 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1265 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1301 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1266 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 1302 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1267 | const error_union_ty = self.air.typeOf(ty_op.operand); | 1303 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1268 | const payload_ty = error_union_ty.errorUnionPayload(); | | |
| 1269 | const mcv = try self.resolveInst(ty_op.operand); | 1304 | const mcv = try self.resolveInst(ty_op.operand); |
| 1270 | if (!payload_ty.hasRuntimeBits()) break :result mcv; | 1305 | break :result try self.errUnionErr(mcv, error_union_ty); |
| 1271 | | | |
| 1272 | return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); | | |
| 1273 | }; | 1306 | }; |
| 1274 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1307 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1275 | } | 1308 | } |
| 1276 | | 1309 | |
| | 1310 | /// Given an error union, returns the payload |
| | 1311 | fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { |
| | 1312 | const payload_ty = error_union_ty.errorUnionPayload(); |
| | 1313 | if (!payload_ty.hasRuntimeBits()) return MCValue.none; |
| | 1314 | |
| | 1315 | const error_ty = error_union_ty.errorUnionSet(); |
| | 1316 | const error_size = @intCast(u32, error_ty.abiSize(self.target.*)); |
| | 1317 | const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); |
| | 1318 | const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); |
| | 1319 | |
| | 1320 | // TODO optimization for small error unions: put into register |
| | 1321 | switch (error_union_mcv) { |
| | 1322 | .register => return self.fail("TODO errUnionPayload for registers", .{}), |
| | 1323 | .stack_argument_offset => |off| { |
| | 1324 | return MCValue{ .stack_argument_offset = off - offset }; |
| | 1325 | }, |
| | 1326 | .stack_offset => |off| { |
| | 1327 | return MCValue{ .stack_offset = off - offset }; |
| | 1328 | }, |
| | 1329 | .memory => |addr| { |
| | 1330 | return MCValue{ .memory = addr - offset }; |
| | 1331 | }, |
| | 1332 | else => unreachable, // invalid MCValue for an error union |
| | 1333 | } |
| | 1334 | } |
| | 1335 | |
| 1277 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | 1336 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1278 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1337 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1279 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 1338 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1280 | const error_union_ty = self.air.typeOf(ty_op.operand); | 1339 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1281 | const payload_ty = error_union_ty.errorUnionPayload(); | 1340 | const mcv = try self.resolveInst(ty_op.operand); |
| 1282 | if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; | 1341 | break :result try self.errUnionPayload(mcv, error_union_ty); |
| 1283 | | | |
| 1284 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); | | |
| 1285 | }; | 1342 | }; |
| 1286 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1343 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1287 | } | 1344 | } |
| ... | @@ -1306,22 +1363,6 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1306,22 +1363,6 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1306 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1363 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1307 | } | 1364 | } |
| 1308 | | 1365 | |
| 1309 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1310 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | | |
| 1311 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | | |
| 1312 | const optional_ty = self.air.typeOfIndex(inst); | | |
| 1313 | const abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); | | |
| 1314 | | | |
| 1315 | // Optional with a zero-bit payload type is just a boolean true | | |
| 1316 | if (abi_size == 1) { | | |
| 1317 | break :result MCValue{ .immediate = 1 }; | | |
| 1318 | } else { | | |
| 1319 | return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); | | |
| 1320 | } | | |
| 1321 | }; | | |
| 1322 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | | |
| 1323 | } | | |
| 1324 | | | |
| 1325 | /// T to E!T | 1366 | /// T to E!T |
| 1326 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { | 1367 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1327 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1368 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | @@ -1343,9 +1384,9 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1343,9 +1384,9 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1343 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1384 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1344 | } | 1385 | } |
| 1345 | | 1386 | |
| 1346 | fn slicePtr(self: *Self, mcv: MCValue) !MCValue { | 1387 | /// Given a slice, returns the length |
| | 1388 | fn slicePtr(mcv: MCValue) MCValue { |
| 1347 | switch (mcv) { | 1389 | switch (mcv) { |
| 1348 | .dead, .unreach => unreachable, | | |
| 1349 | .register => unreachable, // a slice doesn't fit in one register | 1390 | .register => unreachable, // a slice doesn't fit in one register |
| 1350 | .stack_argument_offset => |off| { | 1391 | .stack_argument_offset => |off| { |
| 1351 | return MCValue{ .stack_argument_offset = off }; | 1392 | return MCValue{ .stack_argument_offset = off }; |
| ... | @@ -1356,7 +1397,7 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { | ... | @@ -1356,7 +1397,7 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { |
| 1356 | .memory => |addr| { | 1397 | .memory => |addr| { |
| 1357 | return MCValue{ .memory = addr }; | 1398 | return MCValue{ .memory = addr }; |
| 1358 | }, | 1399 | }, |
| 1359 | else => return self.fail("TODO implement slice_ptr for {}", .{mcv}), | 1400 | else => unreachable, // invalid MCValue for a slice |
| 1360 | } | 1401 | } |
| 1361 | } | 1402 | } |
| 1362 | | 1403 | |
| ... | @@ -1364,7 +1405,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1364,7 +1405,7 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1364 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1405 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1365 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 1406 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1366 | const mcv = try self.resolveInst(ty_op.operand); | 1407 | const mcv = try self.resolveInst(ty_op.operand); |
| 1367 | break :result try self.slicePtr(mcv); | 1408 | break :result slicePtr(mcv); |
| 1368 | }; | 1409 | }; |
| 1369 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1410 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1370 | } | 1411 | } |
| ... | @@ -1444,7 +1485,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1444,7 +1485,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1444 | if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); | 1485 | if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); |
| 1445 | defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); | 1486 | defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); |
| 1446 | | 1487 | |
| 1447 | const base_mcv = try self.slicePtr(slice_mcv); | 1488 | const base_mcv = slicePtr(slice_mcv); |
| 1448 | | 1489 | |
| 1449 | switch (elem_size) { | 1490 | switch (elem_size) { |
| 1450 | 1, 4 => { | 1491 | 1, 4 => { |
| ... | @@ -1507,7 +1548,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1507,7 +1548,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1507 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 1548 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1508 | const slice_mcv = try self.resolveInst(extra.lhs); | 1549 | const slice_mcv = try self.resolveInst(extra.lhs); |
| 1509 | const index_mcv = try self.resolveInst(extra.rhs); | 1550 | const index_mcv = try self.resolveInst(extra.rhs); |
| 1510 | const base_mcv = try self.slicePtr(slice_mcv); | 1551 | const base_mcv = slicePtr(slice_mcv); |
| 1511 | | 1552 | |
| 1512 | const slice_ty = self.air.typeOf(extra.lhs); | 1553 | const slice_ty = self.air.typeOf(extra.lhs); |
| 1513 | | 1554 | |
| ... | @@ -3163,35 +3204,15 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | ... | @@ -3163,35 +3204,15 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3163 | | 3204 | |
| 3164 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 3205 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3165 | const error_type = ty.errorUnionSet(); | 3206 | const error_type = ty.errorUnionSet(); |
| 3166 | const payload_type = ty.errorUnionPayload(); | 3207 | const error_int_type = Type.initTag(.u16); |
| 3167 | | 3208 | |
| 3168 | if (!error_type.hasRuntimeBits()) { | 3209 | if (!error_type.hasRuntimeBits()) { |
| 3169 | return MCValue{ .immediate = 0 }; // always false | 3210 | return MCValue{ .immediate = 0 }; // always false |
| 3170 | } | 3211 | } |
| 3171 | | 3212 | |
| 3172 | if (!payload_type.hasRuntimeBits()) { | 3213 | const error_mcv = try self.errUnionErr(operand, ty); |
| 3173 | if (error_type.abiSize(self.target.*) <= 4) { | 3214 | _ = try self.binOp(.cmp_eq, null, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type); |
| 3174 | const reg_mcv: MCValue = switch (operand) { | 3215 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 3175 | .register => operand, | | |
| 3176 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, | | |
| 3177 | }; | | |
| 3178 | | | |
| 3179 | _ = try self.addInst(.{ | | |
| 3180 | .tag = .cmp, | | |
| 3181 | .data = .{ .rr_op = .{ | | |
| 3182 | .rd = undefined, | | |
| 3183 | .rn = reg_mcv.register, | | |
| 3184 | .op = Instruction.Operand.fromU32(0).?, | | |
| 3185 | } }, | | |
| 3186 | }); | | |
| 3187 | | | |
| 3188 | return MCValue{ .compare_flags_unsigned = .gt }; | | |
| 3189 | } else { | | |
| 3190 | return self.fail("TODO isErr for errors with size > 4", .{}); | | |
| 3191 | } | | |
| 3192 | } else { | | |
| 3193 | return self.fail("TODO isErr for non-empty payloads", .{}); | | |
| 3194 | } | | |
| 3195 | } | 3216 | } |
| 3196 | | 3217 | |
| 3197 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 3218 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| ... | @@ -4044,7 +4065,15 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I | ... | @@ -4044,7 +4065,15 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4044 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); | 4065 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); |
| 4045 | }, | 4066 | }, |
| 4046 | .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), | 4067 | .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), |
| 4047 | .stack_argument_offset => return self.fail("TODO genSetStackArgument src={}", .{mcv}), | 4068 | .stack_argument_offset => |off| { |
| | 4069 | _ = try self.addInst(.{ |
| | 4070 | .tag = .ldr_ptr_stack_argument, |
| | 4071 | .data = .{ .r_stack_offset = .{ |
| | 4072 | .rt = src_reg, |
| | 4073 | .stack_offset = off, |
| | 4074 | } }, |
| | 4075 | }); |
| | 4076 | }, |
| 4048 | else => unreachable, | 4077 | else => unreachable, |
| 4049 | } | 4078 | } |
| 4050 | | 4079 | |