| 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,6 +94,12 @@ pub const Inst = struct { |
| 94 | /// Result type is the same as both operands. | 94 | /// Result type is the same as both operands. |
| 95 | /// Uses the `bin_op` field. | 95 | /// Uses the `bin_op` field. |
| 96 | bit_or, | 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 | /// Bitwise XOR. `^` | 103 | /// Bitwise XOR. `^` |
| 98 | /// Uses the `bin_op` field. | 104 | /// Uses the `bin_op` field. |
| 99 | xor, | 105 | xor, |
| ... | @@ -445,6 +451,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -445,6 +451,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 445 | .xor, | 451 | .xor, |
| 446 | .ptr_add, | 452 | .ptr_add, |
| 447 | .ptr_sub, | 453 | .ptr_sub, |
| 454 | .shr, | ||
| 455 | .shl, | ||
| 448 | => return air.typeOf(datas[inst].bin_op.lhs), | 456 | => return air.typeOf(datas[inst].bin_op.lhs), |
| 449 | 457 | ||
| 450 | .cmp_lt, | 458 | .cmp_lt, |
src/Liveness.zig+2| ... | @@ -249,6 +249,8 @@ fn analyzeInst( | ... | @@ -249,6 +249,8 @@ fn analyzeInst( |
| 249 | .ptr_slice_elem_val, | 249 | .ptr_slice_elem_val, |
| 250 | .ptr_elem_val, | 250 | .ptr_elem_val, |
| 251 | .ptr_ptr_elem_val, | 251 | .ptr_ptr_elem_val, |
| 252 | .shl, | ||
| 253 | .shr, | ||
| 252 | => { | 254 | => { |
| 253 | const o = inst_datas[inst].bin_op; | 255 | const o = inst_datas[inst].bin_op; |
| 254 | return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); | 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,8 +5303,25 @@ fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A |
| 5303 | const tracy = trace(@src()); | 5303 | const tracy = trace(@src()); |
| 5304 | defer tracy.end(); | 5304 | defer tracy.end(); |
| 5305 | 5305 | ||
| 5306 | _ = inst; | 5306 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 5307 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{}); | 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 | fn zirBitwise( | 5327 | fn zirBitwise( |
| ... | @@ -6001,13 +6018,33 @@ fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile | ... | @@ -6001,13 +6018,33 @@ fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile |
| 6001 | fn zirTypeofLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 6018 | fn zirTypeofLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6002 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 6019 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 6003 | const src = inst_data.src(); | 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 | fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 6026 | fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6008 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 6027 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 6009 | const src = inst_data.src(); | 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 | fn zirTypeofPeer( | 6050 | fn zirTypeofPeer( |
src/codegen.zig+20| ... | @@ -822,6 +822,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -822,6 +822,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 822 | .bit_and => try self.airBitAnd(inst), | 822 | .bit_and => try self.airBitAnd(inst), |
| 823 | .bit_or => try self.airBitOr(inst), | 823 | .bit_or => try self.airBitOr(inst), |
| 824 | .xor => try self.airXor(inst), | 824 | .xor => try self.airXor(inst), |
| 825 | .shr => try self.airShr(inst), | ||
| 826 | .shl => try self.airShl(inst), | ||
| 825 | 827 | ||
| 826 | .alloc => try self.airAlloc(inst), | 828 | .alloc => try self.airAlloc(inst), |
| 827 | .arg => try self.airArg(inst), | 829 | .arg => try self.airArg(inst), |
| ... | @@ -1270,6 +1272,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1270,6 +1272,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1270 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | 1293 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1274 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1294 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1275 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | 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,6 +871,9 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM |
| 871 | .bit_or => try airBinOp(o, inst, " | "), | 871 | .bit_or => try airBinOp(o, inst, " | "), |
| 872 | .xor => try airBinOp(o, inst, " ^ "), | 872 | .xor => try airBinOp(o, inst, " ^ "), |
| 873 | 873 | ||
| 874 | .shr => try airBinOp(o, inst, " >> "), | ||
| 875 | .shl => try airBinOp(o, inst, " << "), | ||
| 876 | |||
| 874 | .not => try airNot( o, inst), | 877 | .not => try airNot( o, inst), |
| 875 | 878 | ||
| 876 | .optional_payload => try airOptionalPayload(o, inst), | 879 | .optional_payload => try airOptionalPayload(o, inst), |
src/codegen/llvm.zig+32| ... | @@ -993,6 +993,9 @@ pub const FuncGen = struct { | ... | @@ -993,6 +993,9 @@ pub const FuncGen = struct { |
| 993 | .bit_or, .bool_or => try self.airOr(inst), | 993 | .bit_or, .bool_or => try self.airOr(inst), |
| 994 | .xor => try self.airXor(inst), | 994 | .xor => try self.airXor(inst), |
| 995 | 995 | ||
| 996 | .shl => try self.airShl(inst), | ||
| 997 | .shr => try self.airShr(inst), | ||
| 998 | |||
| 996 | .cmp_eq => try self.airCmp(inst, .eq), | 999 | .cmp_eq => try self.airCmp(inst, .eq), |
| 997 | .cmp_gt => try self.airCmp(inst, .gt), | 1000 | .cmp_gt => try self.airCmp(inst, .gt), |
| 998 | .cmp_gte => try self.airCmp(inst, .gte), | 1001 | .cmp_gte => try self.airCmp(inst, .gte), |
| ... | @@ -1736,6 +1739,35 @@ pub const FuncGen = struct { | ... | @@ -1736,6 +1739,35 @@ pub const FuncGen = struct { |
| 1736 | return self.builder.buildXor(lhs, rhs, ""); | 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 | fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 1771 | fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1740 | if (self.liveness.isUnused(inst)) | 1772 | if (self.liveness.isUnused(inst)) |
| 1741 | return null; | 1773 | return null; |
src/codegen/llvm/bindings.zig+17| ... | @@ -290,6 +290,14 @@ pub const Builder = opaque { | ... | @@ -290,6 +290,14 @@ pub const Builder = opaque { |
| 290 | pub const getInsertBlock = LLVMGetInsertBlock; | 290 | pub const getInsertBlock = LLVMGetInsertBlock; |
| 291 | extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock; | 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 | pub const buildCall = LLVMBuildCall; | 301 | pub const buildCall = LLVMBuildCall; |
| 294 | extern fn LLVMBuildCall( | 302 | extern fn LLVMBuildCall( |
| 295 | *const Builder, | 303 | *const Builder, |
| ... | @@ -381,6 +389,15 @@ pub const Builder = opaque { | ... | @@ -381,6 +389,15 @@ pub const Builder = opaque { |
| 381 | pub const buildAnd = LLVMBuildAnd; | 389 | pub const buildAnd = LLVMBuildAnd; |
| 382 | extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | 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 | pub const buildOr = LLVMBuildOr; | 401 | pub const buildOr = LLVMBuildOr; |
| 385 | extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | 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,6 +127,8 @@ const Writer = struct { |
| 127 | .ptr_slice_elem_val, | 127 | .ptr_slice_elem_val, |
| 128 | .ptr_elem_val, | 128 | .ptr_elem_val, |
| 129 | .ptr_ptr_elem_val, | 129 | .ptr_ptr_elem_val, |
| 130 | .shl, | ||
| 131 | .shr, | ||
| 130 | => try w.writeBinOp(s, inst), | 132 | => try w.writeBinOp(s, inst), |
| 131 | 133 | ||
| 132 | .is_null, | 134 | .is_null, |
test/stage2/cbe.zig+14| ... | @@ -808,6 +808,20 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -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 | var case = ctx.exeFromCompiledC("inferred error sets", .{}); | 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,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 | var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64); | 46 | var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64); |
| 33 | 47 |