| ... | ... | @@ -42,6 +42,11 @@ pub const Reloc = union(enum) { |
| 42 | 42 | /// To perform the reloc, write 32-bit signed little-endian integer |
| 43 | 43 | /// which is a relative jump, based on the address following the reloc. |
| 44 | 44 | rel32: usize, |
| 45 | /// A branch in the ARM instruction set |
| 46 | arm_branch: struct { |
| 47 | pos: usize, |
| 48 | cond: @import("codegen/arm.zig").Condition, |
| 49 | }, |
| 45 | 50 | }; |
| 46 | 51 | |
| 47 | 52 | pub const Result = union(enum) { |
| ... | ... | @@ -1273,11 +1278,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1273 | 1278 | |
| 1274 | 1279 | switch (op) { |
| 1275 | 1280 | .add => { |
| 1276 | | // TODO runtime safety checks (overflow) |
| 1277 | 1281 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, dst_reg, operand).toU32()); |
| 1278 | 1282 | }, |
| 1279 | 1283 | .sub => { |
| 1280 | | // TODO runtime safety checks (underflow) |
| 1281 | 1284 | if (lhs_is_dest) { |
| 1282 | 1285 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, dst_reg, operand).toU32()); |
| 1283 | 1286 | } else { |
| ... | ... | @@ -1293,6 +1296,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1293 | 1296 | .not, .xor => { |
| 1294 | 1297 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, dst_reg, operand).toU32()); |
| 1295 | 1298 | }, |
| 1299 | .cmp_eq => { |
| 1300 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, dst_reg, operand).toU32()); |
| 1301 | }, |
| 1296 | 1302 | else => unreachable, // not a binary instruction |
| 1297 | 1303 | } |
| 1298 | 1304 | } |
| ... | ... | @@ -1953,6 +1959,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1953 | 1959 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 1954 | 1960 | }; |
| 1955 | 1961 | }, |
| 1962 | .arm, .armeb => { |
| 1963 | const lhs = try self.resolveInst(inst.lhs); |
| 1964 | const rhs = try self.resolveInst(inst.rhs); |
| 1965 | |
| 1966 | const src_mcv = rhs; |
| 1967 | const dst_mcv = if (lhs != .register) try self.copyToNewRegister(&inst.base, lhs) else lhs; |
| 1968 | |
| 1969 | try self.genArmBinOpCode(inst.base.src, dst_mcv.register, src_mcv, true, .cmp_eq); |
| 1970 | const info = inst.lhs.ty.intInfo(self.target.*); |
| 1971 | return switch (info.signedness) { |
| 1972 | .signed => MCValue{ .compare_flags_signed = op }, |
| 1973 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 1974 | }; |
| 1975 | }, |
| 1956 | 1976 | else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}), |
| 1957 | 1977 | } |
| 1958 | 1978 | } |
| ... | ... | @@ -2016,6 +2036,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2016 | 2036 | self.code.items.len += 4; |
| 2017 | 2037 | break :reloc reloc; |
| 2018 | 2038 | }, |
| 2039 | .arm, .armeb => reloc: { |
| 2040 | const condition: Condition = switch (cond) { |
| 2041 | .compare_flags_signed => |cmp_op| blk: { |
| 2042 | // Here we map to the opposite condition because the jump is to the false branch. |
| 2043 | const condition = Condition.fromCompareOperatorSigned(cmp_op); |
| 2044 | break :blk condition.negate(); |
| 2045 | }, |
| 2046 | .compare_flags_unsigned => |cmp_op| blk: { |
| 2047 | // Here we map to the opposite condition because the jump is to the false branch. |
| 2048 | const condition = Condition.fromCompareOperatorUnsigned(cmp_op); |
| 2049 | break :blk condition.negate(); |
| 2050 | }, |
| 2051 | .register => |reg| blk: { |
| 2052 | // cmp reg, 1 |
| 2053 | // bne ... |
| 2054 | const op = Instruction.Operand.imm(1, 0); |
| 2055 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32()); |
| 2056 | break :blk .ne; |
| 2057 | }, |
| 2058 | else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }), |
| 2059 | }; |
| 2060 | |
| 2061 | const reloc = Reloc{ |
| 2062 | .arm_branch = .{ |
| 2063 | .pos = self.code.items.len, |
| 2064 | .cond = condition, |
| 2065 | }, |
| 2066 | }; |
| 2067 | try self.code.resize(self.code.items.len + 4); |
| 2068 | break :reloc reloc; |
| 2069 | }, |
| 2019 | 2070 | else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}), |
| 2020 | 2071 | }; |
| 2021 | 2072 | |
| ... | ... | @@ -2175,7 +2226,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2175 | 2226 | } |
| 2176 | 2227 | }, |
| 2177 | 2228 | .arm, .armeb => { |
| 2178 | | if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len))) |delta| { |
| 2229 | if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| { |
| 2179 | 2230 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32()); |
| 2180 | 2231 | } else |err| { |
| 2181 | 2232 | return self.fail(src, "TODO: enable larger branch offset", .{}); |
| ... | ... | @@ -2225,6 +2276,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2225 | 2276 | return self.fail(src, "unable to perform relocation: jump too far", .{}); |
| 2226 | 2277 | mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt); |
| 2227 | 2278 | }, |
| 2279 | .arm_branch => |info| { |
| 2280 | switch (arch) { |
| 2281 | .arm, .armeb => { |
| 2282 | const amt = @intCast(i32, self.code.items.len) - @intCast(i32, info.pos + 8); |
| 2283 | if (math.cast(i26, amt)) |delta| { |
| 2284 | writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32()); |
| 2285 | } else |_| { |
| 2286 | return self.fail(src, "TODO: enable larger branch offset", .{}); |
| 2287 | } |
| 2288 | }, |
| 2289 | else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch |
| 2290 | } |
| 2291 | }, |
| 2228 | 2292 | } |
| 2229 | 2293 | } |
| 2230 | 2294 | |
| ... | ... | @@ -2278,6 +2342,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2278 | 2342 | // Leave the jump offset undefined |
| 2279 | 2343 | block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 }); |
| 2280 | 2344 | }, |
| 2345 | .arm, .armeb => { |
| 2346 | try self.code.resize(self.code.items.len + 4); |
| 2347 | block.codegen.relocs.appendAssumeCapacity(.{ |
| 2348 | .arm_branch = .{ |
| 2349 | .pos = self.code.items.len - 4, |
| 2350 | .cond = .al, |
| 2351 | }, |
| 2352 | }); |
| 2353 | }, |
| 2281 | 2354 | else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}), |
| 2282 | 2355 | } |
| 2283 | 2356 | return .none; |
| ... | ... | @@ -2650,6 +2723,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2650 | 2723 | // Write the debug undefined value. |
| 2651 | 2724 | return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }); |
| 2652 | 2725 | }, |
| 2726 | .compare_flags_unsigned, |
| 2727 | .compare_flags_signed, |
| 2728 | => |op| { |
| 2729 | const condition = switch (mcv) { |
| 2730 | .compare_flags_unsigned => Condition.fromCompareOperatorUnsigned(op), |
| 2731 | .compare_flags_signed => Condition.fromCompareOperatorSigned(op), |
| 2732 | else => unreachable, |
| 2733 | }; |
| 2734 | |
| 2735 | // mov reg, 0 |
| 2736 | // moveq reg, 1 |
| 2737 | const zero = Instruction.Operand.imm(0, 0); |
| 2738 | const one = Instruction.Operand.imm(1, 0); |
| 2739 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, zero).toU32()); |
| 2740 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32()); |
| 2741 | }, |
| 2653 | 2742 | .immediate => |x| { |
| 2654 | 2743 | if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{}); |
| 2655 | 2744 | |