| author | |
| committer | |
| log | 2e6ce11eb29434231102c00fddd0a1b3e0ba5608 |
| tree | e2cec62c094336d3582cb7b5d5c851dcbb04fe18 |
| parent | 7e7d67d8eed45bcf3908edd2f4ca864144fffad5 |
This implements it in the llvm and c backends.
x86_64 will have to be a little more work.10 files changed, 153 insertions(+), 4 deletions(-)
src/Air.zig+8| ... | ... | @@ -94,6 +94,12 @@ pub const Inst = struct { |
| 94 | 94 | /// Result type is the same as both operands. |
| 95 | 95 | /// Uses the `bin_op` field. |
| 96 | 96 | bit_or, |
| 97 | /// Shift right. `>>` | |
| 98 | /// Uses the `bin_op` field. | |
| 99 | shr, | |
| 100 | /// Shift left. `<<` | |
| 101 | /// Uses the `bin_op` field. | |
| 102 | shl, | |
| 97 | 103 | /// Bitwise XOR. `^` |
| 98 | 104 | /// Uses the `bin_op` field. |
| 99 | 105 | xor, |
| ... | ... | @@ -445,6 +451,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 445 | 451 | .xor, |
| 446 | 452 | .ptr_add, |
| 447 | 453 | .ptr_sub, |
| 454 | .shr, | |
| 455 | .shl, | |
| 448 | 456 | => return air.typeOf(datas[inst].bin_op.lhs), |
| 449 | 457 | |
| 450 | 458 | .cmp_lt, |
src/Liveness.zig+2| ... | ... | @@ -249,6 +249,8 @@ fn analyzeInst( |
| 249 | 249 | .ptr_slice_elem_val, |
| 250 | 250 | .ptr_elem_val, |
| 251 | 251 | .ptr_ptr_elem_val, |
| 252 | .shl, | |
| 253 | .shr, | |
| 252 | 254 | => { |
| 253 | 255 | const o = inst_datas[inst].bin_op; |
| 254 | 256 | return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); |
src/Sema.zig+41-4| ... | ... | @@ -5303,8 +5303,25 @@ fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A |
| 5303 | 5303 | const tracy = trace(@src()); |
| 5304 | 5304 | defer tracy.end(); |
| 5305 | 5305 | |
| 5306 | _ = inst; | |
| 5307 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{}); | |
| 5306 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 5307 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | |
| 5308 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | |
| 5309 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | |
| 5310 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 5311 | const lhs = sema.resolveInst(extra.lhs); | |
| 5312 | const rhs = sema.resolveInst(extra.rhs); | |
| 5313 | ||
| 5314 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | |
| 5315 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | |
| 5316 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 5317 | return sema.addConstUndef(sema.typeOf(lhs)); | |
| 5318 | } | |
| 5319 | return sema.mod.fail(&block.base, src, "TODO implement comptime shr", .{}); | |
| 5320 | } | |
| 5321 | } | |
| 5322 | ||
| 5323 | try sema.requireRuntimeBlock(block, src); | |
| 5324 | return block.addBinOp(.shr, lhs, rhs); | |
| 5308 | 5325 | } |
| 5309 | 5326 | |
| 5310 | 5327 | fn zirBitwise( |
| ... | ... | @@ -6001,13 +6018,33 @@ fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile |
| 6001 | 6018 | fn zirTypeofLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6002 | 6019 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 6003 | 6020 | const src = inst_data.src(); |
| 6004 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeofLog2IntType", .{}); | |
| 6021 | const operand = sema.resolveInst(inst_data.operand); | |
| 6022 | const operand_ty = sema.typeOf(operand); | |
| 6023 | return sema.log2IntType(block, operand_ty, src); | |
| 6005 | 6024 | } |
| 6006 | 6025 | |
| 6007 | 6026 | fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6008 | 6027 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 6009 | 6028 | const src = inst_data.src(); |
| 6010 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{}); | |
| 6029 | const operand = try sema.resolveType(block, src, inst_data.operand); | |
| 6030 | return sema.log2IntType(block, operand, src); | |
| 6031 | } | |
| 6032 | ||
| 6033 | fn log2IntType(sema: *Sema, block: *Scope.Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref { | |
| 6034 | if (operand.zigTypeTag() != .Int) return sema.mod.fail( | |
| 6035 | &block.base, | |
| 6036 | src, | |
| 6037 | "bit shifting operation expected integer type, found '{}'", | |
| 6038 | .{operand}, | |
| 6039 | ); | |
| 6040 | ||
| 6041 | var count: u16 = 0; | |
| 6042 | var s = operand.bitSize(sema.mod.getTarget()) - 1; | |
| 6043 | while (s != 0) : (s >>= 1) { | |
| 6044 | count += 1; | |
| 6045 | } | |
| 6046 | const res = try Module.makeIntType(sema.arena, .unsigned, count); | |
| 6047 | return sema.addType(res); | |
| 6011 | 6048 | } |
| 6012 | 6049 | |
| 6013 | 6050 | fn zirTypeofPeer( |
src/codegen.zig+20| ... | ... | @@ -822,6 +822,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 822 | 822 | .bit_and => try self.airBitAnd(inst), |
| 823 | 823 | .bit_or => try self.airBitOr(inst), |
| 824 | 824 | .xor => try self.airXor(inst), |
| 825 | .shr => try self.airShr(inst), | |
| 826 | .shl => try self.airShl(inst), | |
| 825 | 827 | |
| 826 | 828 | .alloc => try self.airAlloc(inst), |
| 827 | 829 | .arg => try self.airArg(inst), |
| ... | ... | @@ -1270,6 +1272,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1270 | 1272 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1271 | 1273 | } |
| 1272 | 1274 | |
| 1275 | fn airShl(self: *Self, inst: Air.Inst.Index) !void { | |
| 1276 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1277 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1278 | .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shl), | |
| 1279 | else => return self.fail("TODO implement shl for {}", .{self.target.cpu.arch}), | |
| 1280 | }; | |
| 1281 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1282 | } | |
| 1283 | ||
| 1284 | fn airShr(self: *Self, inst: Air.Inst.Index) !void { | |
| 1285 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1286 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1287 | .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shr), | |
| 1288 | else => return self.fail("TODO implement shr for {}", .{self.target.cpu.arch}), | |
| 1289 | }; | |
| 1290 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1291 | } | |
| 1292 | ||
| 1273 | 1293 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1274 | 1294 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1275 | 1295 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
src/codegen/c.zig+3| ... | ... | @@ -871,6 +871,9 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM |
| 871 | 871 | .bit_or => try airBinOp(o, inst, " | "), |
| 872 | 872 | .xor => try airBinOp(o, inst, " ^ "), |
| 873 | 873 | |
| 874 | .shr => try airBinOp(o, inst, " >> "), | |
| 875 | .shl => try airBinOp(o, inst, " << "), | |
| 876 | ||
| 874 | 877 | .not => try airNot( o, inst), |
| 875 | 878 | |
| 876 | 879 | .optional_payload => try airOptionalPayload(o, inst), |
src/codegen/llvm.zig+32| ... | ... | @@ -993,6 +993,9 @@ pub const FuncGen = struct { |
| 993 | 993 | .bit_or, .bool_or => try self.airOr(inst), |
| 994 | 994 | .xor => try self.airXor(inst), |
| 995 | 995 | |
| 996 | .shl => try self.airShl(inst), | |
| 997 | .shr => try self.airShr(inst), | |
| 998 | ||
| 996 | 999 | .cmp_eq => try self.airCmp(inst, .eq), |
| 997 | 1000 | .cmp_gt => try self.airCmp(inst, .gt), |
| 998 | 1001 | .cmp_gte => try self.airCmp(inst, .gte), |
| ... | ... | @@ -1736,6 +1739,35 @@ pub const FuncGen = struct { |
| 1736 | 1739 | return self.builder.buildXor(lhs, rhs, ""); |
| 1737 | 1740 | } |
| 1738 | 1741 | |
| 1742 | fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1743 | if (self.liveness.isUnused(inst)) | |
| 1744 | return null; | |
| 1745 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1746 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1747 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1748 | return self.builder.buildShl(lhs, rhs, ""); | |
| 1749 | } | |
| 1750 | ||
| 1751 | fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1752 | if (self.liveness.isUnused(inst)) | |
| 1753 | return null; | |
| 1754 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1755 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1756 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1757 | const lhs_type = self.air.typeOf(bin_op.lhs); | |
| 1758 | const tg = self.dg.module.getTarget(); | |
| 1759 | const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) | |
| 1760 | self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") | |
| 1761 | else | |
| 1762 | rhs; | |
| 1763 | ||
| 1764 | if (self.air.typeOfIndex(inst).isSignedInt()) { | |
| 1765 | return self.builder.buildAShr(lhs, casted_rhs, ""); | |
| 1766 | } else { | |
| 1767 | return self.builder.buildLShr(lhs, casted_rhs, ""); | |
| 1768 | } | |
| 1769 | } | |
| 1770 | ||
| 1739 | 1771 | fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1740 | 1772 | if (self.liveness.isUnused(inst)) |
| 1741 | 1773 | return null; |
src/codegen/llvm/bindings.zig+17| ... | ... | @@ -290,6 +290,14 @@ pub const Builder = opaque { |
| 290 | 290 | pub const getInsertBlock = LLVMGetInsertBlock; |
| 291 | 291 | extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock; |
| 292 | 292 | |
| 293 | pub const buildZExt = LLVMBuildZExt; | |
| 294 | extern fn LLVMBuildZExt( | |
| 295 | *const Builder, | |
| 296 | Value: *const Value, | |
| 297 | DestTy: *const Type, | |
| 298 | Name: [*:0]const u8, | |
| 299 | ) *const Value; | |
| 300 | ||
| 293 | 301 | pub const buildCall = LLVMBuildCall; |
| 294 | 302 | extern fn LLVMBuildCall( |
| 295 | 303 | *const Builder, |
| ... | ... | @@ -381,6 +389,15 @@ pub const Builder = opaque { |
| 381 | 389 | pub const buildAnd = LLVMBuildAnd; |
| 382 | 390 | extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 383 | 391 | |
| 392 | pub const buildLShr = LLVMBuildLShr; | |
| 393 | extern fn LLVMBuildLShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 394 | ||
| 395 | pub const buildAShr = LLVMBuildAShr; | |
| 396 | extern fn LLVMBuildAShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 397 | ||
| 398 | pub const buildShl = LLVMBuildShl; | |
| 399 | extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 400 | ||
| 384 | 401 | pub const buildOr = LLVMBuildOr; |
| 385 | 402 | extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 386 | 403 |
src/print_air.zig+2| ... | ... | @@ -127,6 +127,8 @@ const Writer = struct { |
| 127 | 127 | .ptr_slice_elem_val, |
| 128 | 128 | .ptr_elem_val, |
| 129 | 129 | .ptr_ptr_elem_val, |
| 130 | .shl, | |
| 131 | .shr, | |
| 130 | 132 | => try w.writeBinOp(s, inst), |
| 131 | 133 | |
| 132 | 134 | .is_null, |
test/stage2/cbe.zig+14| ... | ... | @@ -808,6 +808,20 @@ pub fn addCases(ctx: *TestContext) !void { |
| 808 | 808 | }); |
| 809 | 809 | } |
| 810 | 810 | |
| 811 | { | |
| 812 | var case = ctx.exeUsingLlvmBackend("shift right", linux_x64); | |
| 813 | ||
| 814 | case.addCompareOutput( | |
| 815 | \\pub export fn main() void { | |
| 816 | \\ var i: u32 = 16; | |
| 817 | \\ assert(i >> 1, 8); | |
| 818 | \\} | |
| 819 | \\fn assert(a: u32, b: u32) void { | |
| 820 | \\ if (a != b) unreachable; | |
| 821 | \\} | |
| 822 | , ""); | |
| 823 | } | |
| 824 | ||
| 811 | 825 | { |
| 812 | 826 | var case = ctx.exeFromCompiledC("inferred error sets", .{}); |
| 813 | 827 |
test/stage2/llvm.zig+14| ... | ... | @@ -28,6 +28,20 @@ pub fn addCases(ctx: *TestContext) !void { |
| 28 | 28 | , ""); |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | { | |
| 32 | var case = ctx.exeUsingLlvmBackend("shift right", linux_x64); | |
| 33 | ||
| 34 | case.addCompareOutput( | |
| 35 | \\pub export fn main() void { | |
| 36 | \\ var i: u32 = 16; | |
| 37 | \\ assert(i >> 1, 8); | |
| 38 | \\} | |
| 39 | \\fn assert(a: u32, b: u32) void { | |
| 40 | \\ if (a != b) unreachable; | |
| 41 | \\} | |
| 42 | , ""); | |
| 43 | } | |
| 44 | ||
| 31 | 45 | { |
| 32 | 46 | var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64); |
| 33 | 47 |