| ... | @@ -1099,15 +1099,22 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1099,15 +1099,22 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1099 | | 1099 | |
| 1100 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { | 1100 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1101 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1110 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1104 | } | 1111 | } |
| 1105 | | 1112 | |
| 1106 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | 1113 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1107 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1114 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1108 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 1115 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1109 | const err_ty = self.air.typeOf(ty_op.operand); | 1116 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1110 | const payload_ty = err_ty.errorUnionPayload(); | 1117 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 1111 | if (!payload_ty.hasCodeGenBits()) break :result MCValue.none; | 1118 | if (!payload_ty.hasCodeGenBits()) break :result MCValue.none; |
| 1112 | | 1119 | |
| 1113 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); | 1120 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); |
| ... | @@ -2238,10 +2245,16 @@ fn airFence(self: *Self) !void { | ... | @@ -2238,10 +2245,16 @@ fn airFence(self: *Self) !void { |
| 2238 | | 2245 | |
| 2239 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { | 2246 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2240 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 2247 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2241 | const fn_ty = self.air.typeOf(pl_op.operand); | | |
| 2242 | const callee = pl_op.operand; | 2248 | const callee = pl_op.operand; |
| 2243 | const extra = self.air.extraData(Air.Call, pl_op.payload); | 2249 | const extra = self.air.extraData(Air.Call, pl_op.payload); |
| 2244 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); | 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 | var info = try self.resolveCallingConventionValues(fn_ty); | 2259 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2247 | defer info.deinit(self); | 2260 | defer info.deinit(self); |
| ... | @@ -2310,39 +2323,42 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2310,39 +2323,42 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2310 | unreachable; | 2323 | unreachable; |
| 2311 | | 2324 | |
| 2312 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); | 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 | } else if (func_value.castTag(.extern_fn)) |_| { | 2326 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 2340 | return self.fail("TODO implement calling extern functions", .{}); | 2327 | return self.fail("TODO implement calling extern functions", .{}); |
| 2341 | } else { | 2328 | } else { |
| 2342 | return self.fail("TODO implement calling bitcasted functions", .{}); | 2329 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 2343 | } | 2330 | } |
| 2344 | } else { | 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 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 2363 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 2348 | unreachable; // unsupported architecture for MachO | 2364 | unreachable; // unsupported architecture for MachO |
| ... | @@ -2402,19 +2418,26 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2402,19 +2418,26 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2402 | | 2418 | |
| 2403 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | 2419 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2404 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2420 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2405 | if (self.liveness.isUnused(inst)) | 2421 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2406 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 2422 | const lhs = try self.resolveInst(bin_op.lhs); |
| 2407 | const ty = self.air.typeOf(bin_op.lhs); | 2423 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2408 | assert(ty.eql(self.air.typeOf(bin_op.rhs))); | 2424 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 2409 | if (ty.zigTypeTag() == .ErrorSet) | | |
| 2410 | return self.fail("TODO implement cmp for errors", .{}); | | |
| 2411 | | 2425 | |
| 2412 | try self.spillCompareFlagsIfOccupied(); | 2426 | if (lhs_ty.abiSize(self.target.*) > 4) { |
| 2413 | self.compare_flags_inst = inst; | 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 | const lhs_is_register = lhs == .register; | 2441 | const lhs_is_register = lhs == .register; |
| 2419 | const rhs_is_register = rhs == .register; | 2442 | const rhs_is_register = rhs == .register; |
| 2420 | // lhs should always be a register | 2443 | // lhs should always be a register |
| ... | @@ -2448,11 +2471,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -2448,11 +2471,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2448 | // Move the operands to the newly allocated registers | 2471 | // Move the operands to the newly allocated registers |
| 2449 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 2472 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 2450 | if (lhs_mcv == .register and !lhs_is_register) { | 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 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs); | 2475 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs); |
| 2453 | } | 2476 | } |
| 2454 | if (rhs_mcv == .register and !rhs_is_register) { | 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 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs); | 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,9 +2483,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2460 | // The signedness of the integer does not matter for the cmp instruction | 2483 | // The signedness of the integer does not matter for the cmp instruction |
| 2461 | try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); | 2484 | try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); |
| 2462 | | 2485 | |
| 2463 | break :result switch (ty.isSignedInt()) { | 2486 | break :result switch (signedness) { |
| 2464 | true => MCValue{ .compare_flags_signed = op }, | 2487 | .signed => MCValue{ .compare_flags_signed = op }, |
| 2465 | false => MCValue{ .compare_flags_unsigned = op }, | 2488 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 2466 | }; | 2489 | }; |
| 2467 | }; | 2490 | }; |
| 2468 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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,17 +3180,18 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3157 | // TODO optimize the register allocation | 3180 | // TODO optimize the register allocation |
| 3158 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, &.{}); | 3181 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, &.{}); |
| 3159 | const src_reg = regs[0]; | 3182 | const src_reg = regs[0]; |
| 3160 | const dst_reg = regs[2]; | 3183 | const dst_reg = regs[1]; |
| 3161 | const len_reg = regs[2]; | 3184 | const len_reg = regs[2]; |
| 3162 | const count_reg = regs[3]; | 3185 | const count_reg = regs[3]; |
| 3163 | const tmp_reg = regs[4]; | 3186 | const tmp_reg = regs[4]; |
| 3164 | | 3187 | |
| 3165 | // add src_reg, fp, #off | 3188 | // sub src_reg, fp, #off |
| 3166 | const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(off)) |x| x else { | 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 | return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); | 3191 | return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); |
| 3168 | }; | 3192 | }; |
| 3169 | _ = try self.addInst(.{ | 3193 | _ = try self.addInst(.{ |
| 3170 | .tag = .add, | 3194 | .tag = .sub, |
| 3171 | .cond = .al, | 3195 | .cond = .al, |
| 3172 | .data = .{ .rr_op = .{ | 3196 | .data = .{ .rr_op = .{ |
| 3173 | .rd = src_reg, | 3197 | .rd = src_reg, |
| ... | @@ -3177,8 +3201,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -3177,8 +3201,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3177 | }); | 3201 | }); |
| 3178 | | 3202 | |
| 3179 | // sub dst_reg, fp, #stack_offset | 3203 | // sub dst_reg, fp, #stack_offset |
| 3180 | const adj_stack_offset = stack_offset + @intCast(u32, ty.abiSize(self.target.*)); | 3204 | const adj_dst_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 { | 3205 | const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else { |
| 3182 | return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); | 3206 | return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); |
| 3183 | }; | 3207 | }; |
| 3184 | _ = try self.addInst(.{ | 3208 | _ = try self.addInst(.{ |