| author | |
| committer | |
| log | ce7dcf2294297a6037da77b146023311acf8862b |
| tree | fc6648dd0ea42b2201d17e52d10c7d755177462a |
| parent | 53a8e7320510ab64616240ea70ea2e94fcfc02c7 |
| parent | 45a88be573dee8e61490f7d864cc53720981eaff |
| signature |
stage2 ARM: implement basic binary bitwise operations5 files changed, 240 insertions(+), 6 deletions(-)
src/codegen.zig+36-3| ... | @@ -764,6 +764,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -764,6 +764,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 764 | .arg => return self.genArg(inst.castTag(.arg).?), | 764 | .arg => return self.genArg(inst.castTag(.arg).?), |
| 765 | .assembly => return self.genAsm(inst.castTag(.assembly).?), | 765 | .assembly => return self.genAsm(inst.castTag(.assembly).?), |
| 766 | .bitcast => return self.genBitCast(inst.castTag(.bitcast).?), | 766 | .bitcast => return self.genBitCast(inst.castTag(.bitcast).?), |
| 767 | .bitand => return self.genBitAnd(inst.castTag(.bitand).?), | ||
| 768 | .bitor => return self.genBitOr(inst.castTag(.bitor).?), | ||
| 767 | .block => return self.genBlock(inst.castTag(.block).?), | 769 | .block => return self.genBlock(inst.castTag(.block).?), |
| 768 | .br => return self.genBr(inst.castTag(.br).?), | 770 | .br => return self.genBr(inst.castTag(.br).?), |
| 769 | .breakpoint => return self.genBreakpoint(inst.src), | 771 | .breakpoint => return self.genBreakpoint(inst.src), |
| ... | @@ -799,6 +801,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -799,6 +801,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 799 | .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), | 801 | .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), |
| 800 | .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?), | 802 | .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?), |
| 801 | .varptr => return self.genVarPtr(inst.castTag(.varptr).?), | 803 | .varptr => return self.genVarPtr(inst.castTag(.varptr).?), |
| 804 | .xor => return self.genXor(inst.castTag(.xor).?), | ||
| 802 | } | 805 | } |
| 803 | } | 806 | } |
| 804 | 807 | ||
| ... | @@ -1009,6 +1012,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1009,6 +1012,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1009 | } | 1012 | } |
| 1010 | } | 1013 | } |
| 1011 | 1014 | ||
| 1015 | fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | ||
| 1016 | // No side effects, so if it's unreferenced, do nothing. | ||
| 1017 | if (inst.base.isUnused()) | ||
| 1018 | return MCValue.dead; | ||
| 1019 | switch (arch) { | ||
| 1020 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bitand), | ||
| 1021 | else => return self.fail(inst.base.src, "TODO implement bitwise and for {}", .{self.target.cpu.arch}), | ||
| 1022 | } | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | fn genBitOr(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | ||
| 1026 | // No side effects, so if it's unreferenced, do nothing. | ||
| 1027 | if (inst.base.isUnused()) | ||
| 1028 | return MCValue.dead; | ||
| 1029 | switch (arch) { | ||
| 1030 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bitor), | ||
| 1031 | else => return self.fail(inst.base.src, "TODO implement bitwise or for {}", .{self.target.cpu.arch}), | ||
| 1032 | } | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | fn genXor(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | ||
| 1036 | // No side effects, so if it's unreferenced, do nothing. | ||
| 1037 | if (inst.base.isUnused()) | ||
| 1038 | return MCValue.dead; | ||
| 1039 | switch (arch) { | ||
| 1040 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .xor), | ||
| 1041 | else => return self.fail(inst.base.src, "TODO implement xor for {}", .{self.target.cpu.arch}), | ||
| 1042 | } | ||
| 1043 | } | ||
| 1044 | |||
| 1012 | fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | 1045 | fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 1013 | // No side effects, so if it's unreferenced, do nothing. | 1046 | // No side effects, so if it's unreferenced, do nothing. |
| 1014 | if (inst.base.isUnused()) | 1047 | if (inst.base.isUnused()) |
| ... | @@ -1251,13 +1284,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1251,13 +1284,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1251 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, dst_reg, operand).toU32()); | 1284 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, dst_reg, operand).toU32()); |
| 1252 | } | 1285 | } |
| 1253 | }, | 1286 | }, |
| 1254 | .booland => { | 1287 | .booland, .bitand => { |
| 1255 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, dst_reg, operand).toU32()); | 1288 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, dst_reg, operand).toU32()); |
| 1256 | }, | 1289 | }, |
| 1257 | .boolor => { | 1290 | .boolor, .bitor => { |
| 1258 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, dst_reg, operand).toU32()); | 1291 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, dst_reg, operand).toU32()); |
| 1259 | }, | 1292 | }, |
| 1260 | .not => { | 1293 | .not, .xor => { |
| 1261 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, dst_reg, operand).toU32()); | 1294 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, dst_reg, operand).toU32()); |
| 1262 | }, | 1295 | }, |
| 1263 | else => unreachable, // not a binary instruction | 1296 | else => unreachable, // not a binary instruction |
src/ir.zig+6| ... | @@ -56,7 +56,9 @@ pub const Inst = struct { | ... | @@ -56,7 +56,9 @@ pub const Inst = struct { |
| 56 | alloc, | 56 | alloc, |
| 57 | arg, | 57 | arg, |
| 58 | assembly, | 58 | assembly, |
| 59 | bitand, | ||
| 59 | bitcast, | 60 | bitcast, |
| 61 | bitor, | ||
| 60 | block, | 62 | block, |
| 61 | br, | 63 | br, |
| 62 | breakpoint, | 64 | breakpoint, |
| ... | @@ -93,6 +95,7 @@ pub const Inst = struct { | ... | @@ -93,6 +95,7 @@ pub const Inst = struct { |
| 93 | intcast, | 95 | intcast, |
| 94 | unwrap_optional, | 96 | unwrap_optional, |
| 95 | wrap_optional, | 97 | wrap_optional, |
| 98 | xor, | ||
| 96 | switchbr, | 99 | switchbr, |
| 97 | 100 | ||
| 98 | pub fn Type(tag: Tag) type { | 101 | pub fn Type(tag: Tag) type { |
| ... | @@ -130,6 +133,9 @@ pub const Inst = struct { | ... | @@ -130,6 +133,9 @@ pub const Inst = struct { |
| 130 | .store, | 133 | .store, |
| 131 | .booland, | 134 | .booland, |
| 132 | .boolor, | 135 | .boolor, |
| 136 | .bitand, | ||
| 137 | .bitor, | ||
| 138 | .xor, | ||
| 133 | => BinOp, | 139 | => BinOp, |
| 134 | 140 | ||
| 135 | .arg => Arg, | 141 | .arg => Arg, |
src/zir.zig+3| ... | @@ -2342,6 +2342,9 @@ const EmitZIR = struct { | ... | @@ -2342,6 +2342,9 @@ const EmitZIR = struct { |
| 2342 | .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), | 2342 | .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), |
| 2343 | .booland => try self.emitBinOp(inst.src, new_body, inst.castTag(.booland).?, .booland), | 2343 | .booland => try self.emitBinOp(inst.src, new_body, inst.castTag(.booland).?, .booland), |
| 2344 | .boolor => try self.emitBinOp(inst.src, new_body, inst.castTag(.boolor).?, .boolor), | 2344 | .boolor => try self.emitBinOp(inst.src, new_body, inst.castTag(.boolor).?, .boolor), |
| 2345 | .bitand => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitand).?, .bitand), | ||
| 2346 | .bitor => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitor).?, .bitor), | ||
| 2347 | .xor => try self.emitBinOp(inst.src, new_body, inst.castTag(.xor).?, .xor), | ||
| 2345 | 2348 | ||
| 2346 | .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast), | 2349 | .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast), |
| 2347 | .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast), | 2350 | .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast), |
src/zir_sema.zig+61-2| ... | @@ -1459,7 +1459,66 @@ fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError | ... | @@ -1459,7 +1459,66 @@ fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError |
| 1459 | } | 1459 | } |
| 1460 | 1460 | ||
| 1461 | fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | 1461 | fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 1462 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{}); | 1462 | const tracy = trace(@src()); |
| 1463 | defer tracy.end(); | ||
| 1464 | |||
| 1465 | const lhs = try resolveInst(mod, scope, inst.positionals.lhs); | ||
| 1466 | const rhs = try resolveInst(mod, scope, inst.positionals.rhs); | ||
| 1467 | |||
| 1468 | const instructions = &[_]*Inst{ lhs, rhs }; | ||
| 1469 | const resolved_type = try mod.resolvePeerTypes(scope, instructions); | ||
| 1470 | const casted_lhs = try mod.coerce(scope, resolved_type, lhs); | ||
| 1471 | const casted_rhs = try mod.coerce(scope, resolved_type, rhs); | ||
| 1472 | |||
| 1473 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | ||
| 1474 | resolved_type.elemType() | ||
| 1475 | else | ||
| 1476 | resolved_type; | ||
| 1477 | |||
| 1478 | const scalar_tag = scalar_type.zigTypeTag(); | ||
| 1479 | |||
| 1480 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | ||
| 1481 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 1482 | return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ | ||
| 1483 | lhs.ty.arrayLen(), | ||
| 1484 | rhs.ty.arrayLen(), | ||
| 1485 | }); | ||
| 1486 | } | ||
| 1487 | return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBitwise", .{}); | ||
| 1488 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | ||
| 1489 | return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | ||
| 1490 | lhs.ty, | ||
| 1491 | rhs.ty, | ||
| 1492 | }); | ||
| 1493 | } | ||
| 1494 | |||
| 1495 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | ||
| 1496 | |||
| 1497 | if (!is_int) { | ||
| 1498 | return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | if (casted_lhs.value()) |lhs_val| { | ||
| 1502 | if (casted_rhs.value()) |rhs_val| { | ||
| 1503 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 1504 | return mod.constInst(scope, inst.base.src, .{ | ||
| 1505 | .ty = resolved_type, | ||
| 1506 | .val = Value.initTag(.undef), | ||
| 1507 | }); | ||
| 1508 | } | ||
| 1509 | return mod.fail(scope, inst.base.src, "TODO implement comptime bitwise operations", .{}); | ||
| 1510 | } | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | ||
| 1514 | const ir_tag = switch (inst.base.tag) { | ||
| 1515 | .bitand => Inst.Tag.bitand, | ||
| 1516 | .bitor => Inst.Tag.bitor, | ||
| 1517 | .xor => Inst.Tag.xor, | ||
| 1518 | else => unreachable, | ||
| 1519 | }; | ||
| 1520 | |||
| 1521 | return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); | ||
| 1463 | } | 1522 | } |
| 1464 | 1523 | ||
| 1465 | fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 1524 | fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| ... | @@ -1502,7 +1561,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn | ... | @@ -1502,7 +1561,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn |
| 1502 | } | 1561 | } |
| 1503 | return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); | 1562 | return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); |
| 1504 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | 1563 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { |
| 1505 | return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | 1564 | return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ |
| 1506 | lhs.ty, | 1565 | lhs.ty, |
| 1507 | rhs.ty, | 1566 | rhs.ty, |
| 1508 | }); | 1567 | }); |
test/stage2/arm.zig+134-1| ... | @@ -115,7 +115,8 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -115,7 +115,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | { | 117 | { |
| 118 | var case = ctx.exe("addition", linux_arm); | 118 | var case = ctx.exe("arithmetic operations", linux_arm); |
| 119 | |||
| 119 | // Add two numbers | 120 | // Add two numbers |
| 120 | case.addCompareOutput( | 121 | case.addCompareOutput( |
| 121 | \\export fn _start() noreturn { | 122 | \\export fn _start() noreturn { |
| ... | @@ -148,5 +149,137 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -148,5 +149,137 @@ pub fn addCases(ctx: *TestContext) !void { |
| 148 | , | 149 | , |
| 149 | "12345612345678", | 150 | "12345612345678", |
| 150 | ); | 151 | ); |
| 152 | |||
| 153 | // Subtract two numbers | ||
| 154 | case.addCompareOutput( | ||
| 155 | \\export fn _start() noreturn { | ||
| 156 | \\ print(10, 5); | ||
| 157 | \\ print(4, 3); | ||
| 158 | \\ exit(); | ||
| 159 | \\} | ||
| 160 | \\ | ||
| 161 | \\fn print(a: u32, b: u32) void { | ||
| 162 | \\ asm volatile ("svc #0" | ||
| 163 | \\ : | ||
| 164 | \\ : [number] "{r7}" (4), | ||
| 165 | \\ [arg3] "{r2}" (a - b), | ||
| 166 | \\ [arg1] "{r0}" (1), | ||
| 167 | \\ [arg2] "{r1}" (@ptrToInt("123456789")) | ||
| 168 | \\ : "memory" | ||
| 169 | \\ ); | ||
| 170 | \\ return; | ||
| 171 | \\} | ||
| 172 | \\ | ||
| 173 | \\fn exit() noreturn { | ||
| 174 | \\ asm volatile ("svc #0" | ||
| 175 | \\ : | ||
| 176 | \\ : [number] "{r7}" (1), | ||
| 177 | \\ [arg1] "{r0}" (0) | ||
| 178 | \\ : "memory" | ||
| 179 | \\ ); | ||
| 180 | \\ unreachable; | ||
| 181 | \\} | ||
| 182 | , | ||
| 183 | "123451", | ||
| 184 | ); | ||
| 185 | |||
| 186 | // Bitwise And | ||
| 187 | case.addCompareOutput( | ||
| 188 | \\export fn _start() noreturn { | ||
| 189 | \\ print(8, 9); | ||
| 190 | \\ print(3, 7); | ||
| 191 | \\ exit(); | ||
| 192 | \\} | ||
| 193 | \\ | ||
| 194 | \\fn print(a: u32, b: u32) void { | ||
| 195 | \\ asm volatile ("svc #0" | ||
| 196 | \\ : | ||
| 197 | \\ : [number] "{r7}" (4), | ||
| 198 | \\ [arg3] "{r2}" (a & b), | ||
| 199 | \\ [arg1] "{r0}" (1), | ||
| 200 | \\ [arg2] "{r1}" (@ptrToInt("123456789")) | ||
| 201 | \\ : "memory" | ||
| 202 | \\ ); | ||
| 203 | \\ return; | ||
| 204 | \\} | ||
| 205 | \\ | ||
| 206 | \\fn exit() noreturn { | ||
| 207 | \\ asm volatile ("svc #0" | ||
| 208 | \\ : | ||
| 209 | \\ : [number] "{r7}" (1), | ||
| 210 | \\ [arg1] "{r0}" (0) | ||
| 211 | \\ : "memory" | ||
| 212 | \\ ); | ||
| 213 | \\ unreachable; | ||
| 214 | \\} | ||
| 215 | , | ||
| 216 | "12345678123", | ||
| 217 | ); | ||
| 218 | |||
| 219 | // Bitwise Or | ||
| 220 | case.addCompareOutput( | ||
| 221 | \\export fn _start() noreturn { | ||
| 222 | \\ print(4, 2); | ||
| 223 | \\ print(3, 7); | ||
| 224 | \\ exit(); | ||
| 225 | \\} | ||
| 226 | \\ | ||
| 227 | \\fn print(a: u32, b: u32) void { | ||
| 228 | \\ asm volatile ("svc #0" | ||
| 229 | \\ : | ||
| 230 | \\ : [number] "{r7}" (4), | ||
| 231 | \\ [arg3] "{r2}" (a | b), | ||
| 232 | \\ [arg1] "{r0}" (1), | ||
| 233 | \\ [arg2] "{r1}" (@ptrToInt("123456789")) | ||
| 234 | \\ : "memory" | ||
| 235 | \\ ); | ||
| 236 | \\ return; | ||
| 237 | \\} | ||
| 238 | \\ | ||
| 239 | \\fn exit() noreturn { | ||
| 240 | \\ asm volatile ("svc #0" | ||
| 241 | \\ : | ||
| 242 | \\ : [number] "{r7}" (1), | ||
| 243 | \\ [arg1] "{r0}" (0) | ||
| 244 | \\ : "memory" | ||
| 245 | \\ ); | ||
| 246 | \\ unreachable; | ||
| 247 | \\} | ||
| 248 | , | ||
| 249 | "1234561234567", | ||
| 250 | ); | ||
| 251 | |||
| 252 | // Bitwise Xor | ||
| 253 | case.addCompareOutput( | ||
| 254 | \\export fn _start() noreturn { | ||
| 255 | \\ print(42, 42); | ||
| 256 | \\ print(3, 5); | ||
| 257 | \\ exit(); | ||
| 258 | \\} | ||
| 259 | \\ | ||
| 260 | \\fn print(a: u32, b: u32) void { | ||
| 261 | \\ asm volatile ("svc #0" | ||
| 262 | \\ : | ||
| 263 | \\ : [number] "{r7}" (4), | ||
| 264 | \\ [arg3] "{r2}" (a ^ b), | ||
| 265 | \\ [arg1] "{r0}" (1), | ||
| 266 | \\ [arg2] "{r1}" (@ptrToInt("123456789")) | ||
| 267 | \\ : "memory" | ||
| 268 | \\ ); | ||
| 269 | \\ return; | ||
| 270 | \\} | ||
| 271 | \\ | ||
| 272 | \\fn exit() noreturn { | ||
| 273 | \\ asm volatile ("svc #0" | ||
| 274 | \\ : | ||
| 275 | \\ : [number] "{r7}" (1), | ||
| 276 | \\ [arg1] "{r0}" (0) | ||
| 277 | \\ : "memory" | ||
| 278 | \\ ); | ||
| 279 | \\ unreachable; | ||
| 280 | \\} | ||
| 281 | , | ||
| 282 | "123456", | ||
| 283 | ); | ||
| 151 | } | 284 | } |
| 152 | } | 285 | } |