| ... | ... | @@ -1261,27 +1261,84 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1261 | 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 | 1300 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1265 | 1301 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1266 | 1302 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1267 | 1303 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1268 | | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1269 | 1304 | const mcv = try self.resolveInst(ty_op.operand); |
| 1270 | | if (!payload_ty.hasRuntimeBits()) break :result mcv; |
| 1271 | | |
| 1272 | | return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); |
| 1305 | break :result try self.errUnionErr(mcv, error_union_ty); |
| 1273 | 1306 | }; |
| 1274 | 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 | 1336 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1278 | 1337 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1279 | 1338 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1280 | 1339 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1281 | | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1282 | | if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; |
| 1283 | | |
| 1284 | | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); |
| 1340 | const mcv = try self.resolveInst(ty_op.operand); |
| 1341 | break :result try self.errUnionPayload(mcv, error_union_ty); |
| 1285 | 1342 | }; |
| 1286 | 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 | 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 | 1366 | /// T to E!T |
| 1326 | 1367 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1327 | 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 | 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 | 1389 | switch (mcv) { |
| 1348 | | .dead, .unreach => unreachable, |
| 1349 | 1390 | .register => unreachable, // a slice doesn't fit in one register |
| 1350 | 1391 | .stack_argument_offset => |off| { |
| 1351 | 1392 | return MCValue{ .stack_argument_offset = off }; |
| ... | ... | @@ -1356,7 +1397,7 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { |
| 1356 | 1397 | .memory => |addr| { |
| 1357 | 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 | 1405 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1365 | 1406 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1366 | 1407 | const mcv = try self.resolveInst(ty_op.operand); |
| 1367 | | break :result try self.slicePtr(mcv); |
| 1408 | break :result slicePtr(mcv); |
| 1368 | 1409 | }; |
| 1369 | 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 | 1485 | if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); |
| 1445 | 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 | 1490 | switch (elem_size) { |
| 1450 | 1491 | 1, 4 => { |
| ... | ... | @@ -1507,7 +1548,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1507 | 1548 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1508 | 1549 | const slice_mcv = try self.resolveInst(extra.lhs); |
| 1509 | 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 | 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 | 3204 | |
| 3164 | 3205 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3165 | 3206 | const error_type = ty.errorUnionSet(); |
| 3166 | | const payload_type = ty.errorUnionPayload(); |
| 3207 | const error_int_type = Type.initTag(.u16); |
| 3167 | 3208 | |
| 3168 | 3209 | if (!error_type.hasRuntimeBits()) { |
| 3169 | 3210 | return MCValue{ .immediate = 0 }; // always false |
| 3170 | 3211 | } |
| 3171 | 3212 | |
| 3172 | | if (!payload_type.hasRuntimeBits()) { |
| 3173 | | if (error_type.abiSize(self.target.*) <= 4) { |
| 3174 | | const reg_mcv: MCValue = switch (operand) { |
| 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 | | } |
| 3213 | const error_mcv = try self.errUnionErr(operand, ty); |
| 3214 | _ = try self.binOp(.cmp_eq, null, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type); |
| 3215 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 3195 | 3216 | } |
| 3196 | 3217 | |
| 3197 | 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 | 4065 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); |
| 4045 | 4066 | }, |
| 4046 | 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 | 4077 | else => unreachable, |
| 4049 | 4078 | } |
| 4050 | 4079 | |