| ... | ... | @@ -1099,15 +1099,22 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1099 | 1099 | |
| 1100 | 1100 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1101 | 1101 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1102 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}); |
| 1102 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1103 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1104 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1105 | const mcv = try self.resolveInst(ty_op.operand); |
| 1106 | if (!payload_ty.hasCodeGenBits()) break :result mcv; |
| 1107 | |
| 1108 | return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); |
| 1109 | }; |
| 1103 | 1110 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1104 | 1111 | } |
| 1105 | 1112 | |
| 1106 | 1113 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1107 | 1114 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1108 | 1115 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1109 | | const err_ty = self.air.typeOf(ty_op.operand); |
| 1110 | | const payload_ty = err_ty.errorUnionPayload(); |
| 1116 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1117 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1111 | 1118 | if (!payload_ty.hasCodeGenBits()) break :result MCValue.none; |
| 1112 | 1119 | |
| 1113 | 1120 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); |
| ... | ... | @@ -2238,10 +2245,16 @@ fn airFence(self: *Self) !void { |
| 2238 | 2245 | |
| 2239 | 2246 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2240 | 2247 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2241 | | const fn_ty = self.air.typeOf(pl_op.operand); |
| 2242 | 2248 | const callee = pl_op.operand; |
| 2243 | 2249 | const extra = self.air.extraData(Air.Call, pl_op.payload); |
| 2244 | 2250 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); |
| 2251 | const ty = self.air.typeOf(callee); |
| 2252 | |
| 2253 | const fn_ty = switch (ty.zigTypeTag()) { |
| 2254 | .Fn => ty, |
| 2255 | .Pointer => ty.childType(), |
| 2256 | else => unreachable, |
| 2257 | }; |
| 2245 | 2258 | |
| 2246 | 2259 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2247 | 2260 | defer info.deinit(self); |
| ... | ... | @@ -2310,39 +2323,42 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2310 | 2323 | unreachable; |
| 2311 | 2324 | |
| 2312 | 2325 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); |
| 2313 | | |
| 2314 | | // TODO: add Instruction.supportedOn |
| 2315 | | // function for ARM |
| 2316 | | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { |
| 2317 | | _ = try self.addInst(.{ |
| 2318 | | .tag = .blx, |
| 2319 | | .cond = .al, |
| 2320 | | .data = .{ .reg = .lr }, |
| 2321 | | }); |
| 2322 | | } else { |
| 2323 | | return self.fail("TODO fix blx emulatio for ARM <v5", .{}); |
| 2324 | | // _ = try self.addInst(.{ |
| 2325 | | // .tag = .mov, |
| 2326 | | // .cond = .al, |
| 2327 | | // .data = .{ .rr_op = .{ |
| 2328 | | // .rd = .lr, |
| 2329 | | // .rn = .r0, |
| 2330 | | // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none), |
| 2331 | | // } }, |
| 2332 | | // }); |
| 2333 | | // _ = try self.addInst(.{ |
| 2334 | | // .tag = .bx, |
| 2335 | | // .cond = .al, |
| 2336 | | // .data = .{ .reg = .lr }, |
| 2337 | | // }); |
| 2338 | | } |
| 2339 | 2326 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 2340 | 2327 | return self.fail("TODO implement calling extern functions", .{}); |
| 2341 | 2328 | } else { |
| 2342 | 2329 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 2343 | 2330 | } |
| 2344 | 2331 | } else { |
| 2345 | | return self.fail("TODO implement calling runtime known function pointer", .{}); |
| 2332 | assert(ty.zigTypeTag() == .Pointer); |
| 2333 | const mcv = try self.resolveInst(callee); |
| 2334 | |
| 2335 | try self.genSetReg(Type.initTag(.usize), .lr, mcv); |
| 2336 | } |
| 2337 | |
| 2338 | // TODO: add Instruction.supportedOn |
| 2339 | // function for ARM |
| 2340 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { |
| 2341 | _ = try self.addInst(.{ |
| 2342 | .tag = .blx, |
| 2343 | .cond = .al, |
| 2344 | .data = .{ .reg = .lr }, |
| 2345 | }); |
| 2346 | } else { |
| 2347 | return self.fail("TODO fix blx emulation for ARM <v5", .{}); |
| 2348 | // _ = try self.addInst(.{ |
| 2349 | // .tag = .mov, |
| 2350 | // .cond = .al, |
| 2351 | // .data = .{ .rr_op = .{ |
| 2352 | // .rd = .lr, |
| 2353 | // .rn = .r0, |
| 2354 | // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none), |
| 2355 | // } }, |
| 2356 | // }); |
| 2357 | // _ = try self.addInst(.{ |
| 2358 | // .tag = .bx, |
| 2359 | // .cond = .al, |
| 2360 | // .data = .{ .reg = .lr }, |
| 2361 | // }); |
| 2346 | 2362 | } |
| 2347 | 2363 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 2348 | 2364 | unreachable; // unsupported architecture for MachO |
| ... | ... | @@ -2402,19 +2418,26 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2402 | 2418 | |
| 2403 | 2419 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2404 | 2420 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2405 | | if (self.liveness.isUnused(inst)) |
| 2406 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2407 | | const ty = self.air.typeOf(bin_op.lhs); |
| 2408 | | assert(ty.eql(self.air.typeOf(bin_op.rhs))); |
| 2409 | | if (ty.zigTypeTag() == .ErrorSet) |
| 2410 | | return self.fail("TODO implement cmp for errors", .{}); |
| 2421 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2422 | const lhs = try self.resolveInst(bin_op.lhs); |
| 2423 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2424 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 2411 | 2425 | |
| 2412 | | try self.spillCompareFlagsIfOccupied(); |
| 2413 | | self.compare_flags_inst = inst; |
| 2426 | if (lhs_ty.abiSize(self.target.*) > 4) { |
| 2427 | return self.fail("TODO cmp for types with size > 4", .{}); |
| 2428 | } |
| 2429 | |
| 2430 | const signedness: std.builtin.Signedness = blk: { |
| 2431 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2432 | if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 2433 | |
| 2434 | // incase of an actual integer, we emit the correct signedness |
| 2435 | break :blk lhs_ty.intInfo(self.target.*).signedness; |
| 2436 | }; |
| 2437 | |
| 2438 | try self.spillCompareFlagsIfOccupied(); |
| 2439 | self.compare_flags_inst = inst; |
| 2414 | 2440 | |
| 2415 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 2416 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 2417 | | const result: MCValue = result: { |
| 2418 | 2441 | const lhs_is_register = lhs == .register; |
| 2419 | 2442 | const rhs_is_register = rhs == .register; |
| 2420 | 2443 | // lhs should always be a register |
| ... | ... | @@ -2448,11 +2471,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2448 | 2471 | // Move the operands to the newly allocated registers |
| 2449 | 2472 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 2450 | 2473 | if (lhs_mcv == .register and !lhs_is_register) { |
| 2451 | | try self.genSetReg(ty, lhs_mcv.register, lhs); |
| 2474 | try self.genSetReg(lhs_ty, lhs_mcv.register, lhs); |
| 2452 | 2475 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs); |
| 2453 | 2476 | } |
| 2454 | 2477 | if (rhs_mcv == .register and !rhs_is_register) { |
| 2455 | | try self.genSetReg(ty, rhs_mcv.register, rhs); |
| 2478 | try self.genSetReg(lhs_ty, rhs_mcv.register, rhs); |
| 2456 | 2479 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs); |
| 2457 | 2480 | } |
| 2458 | 2481 | |
| ... | ... | @@ -2460,9 +2483,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2460 | 2483 | // The signedness of the integer does not matter for the cmp instruction |
| 2461 | 2484 | try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); |
| 2462 | 2485 | |
| 2463 | | break :result switch (ty.isSignedInt()) { |
| 2464 | | true => MCValue{ .compare_flags_signed = op }, |
| 2465 | | false => MCValue{ .compare_flags_unsigned = op }, |
| 2486 | break :result switch (signedness) { |
| 2487 | .signed => MCValue{ .compare_flags_signed = op }, |
| 2488 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 2466 | 2489 | }; |
| 2467 | 2490 | }; |
| 2468 | 2491 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| ... | ... | @@ -3157,17 +3180,18 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3157 | 3180 | // TODO optimize the register allocation |
| 3158 | 3181 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, &.{}); |
| 3159 | 3182 | const src_reg = regs[0]; |
| 3160 | | const dst_reg = regs[2]; |
| 3183 | const dst_reg = regs[1]; |
| 3161 | 3184 | const len_reg = regs[2]; |
| 3162 | 3185 | const count_reg = regs[3]; |
| 3163 | 3186 | const tmp_reg = regs[4]; |
| 3164 | 3187 | |
| 3165 | | // add src_reg, fp, #off |
| 3166 | | const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(off)) |x| x else { |
| 3188 | // sub src_reg, fp, #off |
| 3189 | const adj_src_offset = off + @intCast(u32, ty.abiSize(self.target.*)); |
| 3190 | const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_src_offset)) |x| x else { |
| 3167 | 3191 | return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); |
| 3168 | 3192 | }; |
| 3169 | 3193 | _ = try self.addInst(.{ |
| 3170 | | .tag = .add, |
| 3194 | .tag = .sub, |
| 3171 | 3195 | .cond = .al, |
| 3172 | 3196 | .data = .{ .rr_op = .{ |
| 3173 | 3197 | .rd = src_reg, |
| ... | ... | @@ -3177,8 +3201,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3177 | 3201 | }); |
| 3178 | 3202 | |
| 3179 | 3203 | // sub dst_reg, fp, #stack_offset |
| 3180 | | const adj_stack_offset = stack_offset + @intCast(u32, ty.abiSize(self.target.*)); |
| 3181 | | const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_stack_offset)) |x| x else { |
| 3204 | const adj_dst_offset = stack_offset + @intCast(u32, ty.abiSize(self.target.*)); |
| 3205 | const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else { |
| 3182 | 3206 | return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); |
| 3183 | 3207 | }; |
| 3184 | 3208 | _ = try self.addInst(.{ |