From 2e6ce11eb29434231102c00fddd0a1b3e0ba5608 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Wed, 18 Aug 2021 22:22:12 -0400 Subject: [PATCH 1/2] stage2: implement shr and boilerplate for shl This implements it in the llvm and c backends. x86_64 will have to be a little more work. --- src/Air.zig | 8 +++++++ src/Liveness.zig | 2 ++ src/Sema.zig | 45 +++++++++++++++++++++++++++++++---- src/codegen.zig | 20 ++++++++++++++++ src/codegen/c.zig | 3 +++ src/codegen/llvm.zig | 32 +++++++++++++++++++++++++ src/codegen/llvm/bindings.zig | 17 +++++++++++++ src/print_air.zig | 2 ++ test/stage2/cbe.zig | 14 +++++++++++ test/stage2/llvm.zig | 14 +++++++++++ 10 files changed, 153 insertions(+), 4 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 391683afd5..2c5235e4a1 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -94,6 +94,12 @@ pub const Inst = struct { /// Result type is the same as both operands. /// Uses the `bin_op` field. bit_or, + /// Shift right. `>>` + /// Uses the `bin_op` field. + shr, + /// Shift left. `<<` + /// Uses the `bin_op` field. + shl, /// Bitwise XOR. `^` /// Uses the `bin_op` field. xor, @@ -445,6 +451,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .xor, .ptr_add, .ptr_sub, + .shr, + .shl, => return air.typeOf(datas[inst].bin_op.lhs), .cmp_lt, diff --git a/src/Liveness.zig b/src/Liveness.zig index 48603fc7c9..240c82bea5 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -249,6 +249,8 @@ fn analyzeInst( .ptr_slice_elem_val, .ptr_elem_val, .ptr_ptr_elem_val, + .shl, + .shr, => { const o = inst_datas[inst].bin_op; return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index 8b6f6d4a9f..d2b2a665e6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5303,8 +5303,25 @@ fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A const tracy = trace(@src()); defer tracy.end(); - _ = inst; - return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{}); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; + const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); + + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { + if (lhs_val.isUndef() or rhs_val.isUndef()) { + return sema.addConstUndef(sema.typeOf(lhs)); + } + return sema.mod.fail(&block.base, src, "TODO implement comptime shr", .{}); + } + } + + try sema.requireRuntimeBlock(block, src); + return block.addBinOp(.shr, lhs, rhs); } fn zirBitwise( @@ -6001,13 +6018,33 @@ fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile fn zirTypeofLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeofLog2IntType", .{}); + const operand = sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + return sema.log2IntType(block, operand_ty, src); } fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{}); + const operand = try sema.resolveType(block, src, inst_data.operand); + return sema.log2IntType(block, operand, src); +} + +fn log2IntType(sema: *Sema, block: *Scope.Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref { + if (operand.zigTypeTag() != .Int) return sema.mod.fail( + &block.base, + src, + "bit shifting operation expected integer type, found '{}'", + .{operand}, + ); + + var count: u16 = 0; + var s = operand.bitSize(sema.mod.getTarget()) - 1; + while (s != 0) : (s >>= 1) { + count += 1; + } + const res = try Module.makeIntType(sema.arena, .unsigned, count); + return sema.addType(res); } fn zirTypeofPeer( diff --git a/src/codegen.zig b/src/codegen.zig index d5b106dbe3..584b2bf8f4 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -822,6 +822,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .bit_and => try self.airBitAnd(inst), .bit_or => try self.airBitOr(inst), .xor => try self.airXor(inst), + .shr => try self.airShr(inst), + .shl => try self.airShl(inst), .alloc => try self.airAlloc(inst), .arg => try self.airArg(inst), @@ -1270,6 +1272,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } + fn airShl(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { + .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shl), + else => return self.fail("TODO implement shl for {}", .{self.target.cpu.arch}), + }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + fn airShr(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { + .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shr), + else => return self.fail("TODO implement shr for {}", .{self.target.cpu.arch}), + }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); + } + fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a67e2438c2..d3417d1567 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -871,6 +871,9 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM .bit_or => try airBinOp(o, inst, " | "), .xor => try airBinOp(o, inst, " ^ "), + .shr => try airBinOp(o, inst, " >> "), + .shl => try airBinOp(o, inst, " << "), + .not => try airNot( o, inst), .optional_payload => try airOptionalPayload(o, inst), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 4f9a24f80c..4b5ac9efe9 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -993,6 +993,9 @@ pub const FuncGen = struct { .bit_or, .bool_or => try self.airOr(inst), .xor => try self.airXor(inst), + .shl => try self.airShl(inst), + .shr => try self.airShr(inst), + .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), .cmp_gte => try self.airCmp(inst, .gte), @@ -1736,6 +1739,35 @@ pub const FuncGen = struct { return self.builder.buildXor(lhs, rhs, ""); } + fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + return self.builder.buildShl(lhs, rhs, ""); + } + + fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_type = self.air.typeOf(bin_op.lhs); + const tg = self.dg.module.getTarget(); + const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + else + rhs; + + if (self.air.typeOfIndex(inst).isSignedInt()) { + return self.builder.buildAShr(lhs, casted_rhs, ""); + } else { + return self.builder.buildLShr(lhs, casted_rhs, ""); + } + } + fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index da02e56ac8..d33ca29d4f 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -290,6 +290,14 @@ pub const Builder = opaque { pub const getInsertBlock = LLVMGetInsertBlock; extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock; + pub const buildZExt = LLVMBuildZExt; + extern fn LLVMBuildZExt( + *const Builder, + Value: *const Value, + DestTy: *const Type, + Name: [*:0]const u8, + ) *const Value; + pub const buildCall = LLVMBuildCall; extern fn LLVMBuildCall( *const Builder, @@ -381,6 +389,15 @@ pub const Builder = opaque { pub const buildAnd = LLVMBuildAnd; extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildLShr = LLVMBuildLShr; + extern fn LLVMBuildLShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildAShr = LLVMBuildAShr; + extern fn LLVMBuildAShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildShl = LLVMBuildShl; + extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildOr = LLVMBuildOr; extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; diff --git a/src/print_air.zig b/src/print_air.zig index 66490b6512..738453464b 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -127,6 +127,8 @@ const Writer = struct { .ptr_slice_elem_val, .ptr_elem_val, .ptr_ptr_elem_val, + .shl, + .shr, => try w.writeBinOp(s, inst), .is_null, diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index aa1022257b..5025d2ad9f 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -808,6 +808,20 @@ pub fn addCases(ctx: *TestContext) !void { }); } + { + var case = ctx.exeUsingLlvmBackend("shift right", linux_x64); + + case.addCompareOutput( + \\pub export fn main() void { + \\ var i: u32 = 16; + \\ assert(i >> 1, 8); + \\} + \\fn assert(a: u32, b: u32) void { + \\ if (a != b) unreachable; + \\} + , ""); + } + { var case = ctx.exeFromCompiledC("inferred error sets", .{}); diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index 0e8e0036e3..e49641b997 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -28,6 +28,20 @@ pub fn addCases(ctx: *TestContext) !void { , ""); } + { + var case = ctx.exeUsingLlvmBackend("shift right", linux_x64); + + case.addCompareOutput( + \\pub export fn main() void { + \\ var i: u32 = 16; + \\ assert(i >> 1, 8); + \\} + \\fn assert(a: u32, b: u32) void { + \\ if (a != b) unreachable; + \\} + , ""); + } + { var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64); From 2e22f7e5a5ff51aa4a1672e011cdc07b08a9a661 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Thu, 19 Aug 2021 13:04:52 -0400 Subject: [PATCH 2/2] stage2: implement shl This is implemented in the llvm and cbe backends. x86_64 will take a bit more time. --- src/Sema.zig | 22 +++++++++++++++++++--- src/codegen/llvm.zig | 8 +++++++- test/stage2/cbe.zig | 17 ++++++++++++++--- test/stage2/llvm.zig | 11 ++++++++++- 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d2b2a665e6..360d936b61 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5294,9 +5294,25 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A const tracy = trace(@src()); defer tracy.end(); - _ = block; - _ = inst; - return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{}); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; + const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); + + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { + if (lhs_val.isUndef() or rhs_val.isUndef()) { + return sema.addConstUndef(sema.typeOf(lhs)); + } + return sema.mod.fail(&block.base, src, "TODO implement comptime shl", .{}); + } + } + + try sema.requireRuntimeBlock(block, src); + return block.addBinOp(.shl, lhs, rhs); } fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 4b5ac9efe9..632b275704 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1745,7 +1745,13 @@ pub const FuncGen = struct { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - return self.builder.buildShl(lhs, rhs, ""); + const lhs_type = self.air.typeOf(bin_op.lhs); + const tg = self.dg.module.getTarget(); + const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + else + rhs; + return self.builder.buildShl(lhs, casted_rhs, ""); } fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 5025d2ad9f..c2c82c20e6 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -809,12 +809,23 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = ctx.exeUsingLlvmBackend("shift right", linux_x64); - + var case = ctx.exeFromCompiledC("shift right + left", .{}); case.addCompareOutput( - \\pub export fn main() void { + \\pub export fn main() c_int { \\ var i: u32 = 16; \\ assert(i >> 1, 8); + \\ return 0; + \\} + \\fn assert(a: u32, b: u32) void { + \\ if (a != b) unreachable; + \\} + , ""); + + case.addCompareOutput( + \\pub export fn main() c_int { + \\ var i: u32 = 16; + \\ assert(i << 1, 32); + \\ return 0; \\} \\fn assert(a: u32, b: u32) void { \\ if (a != b) unreachable; diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index e49641b997..9e6e938a8c 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -29,7 +29,7 @@ pub fn addCases(ctx: *TestContext) !void { } { - var case = ctx.exeUsingLlvmBackend("shift right", linux_x64); + var case = ctx.exeUsingLlvmBackend("shift right + left", linux_x64); case.addCompareOutput( \\pub export fn main() void { @@ -40,6 +40,15 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (a != b) unreachable; \\} , ""); + case.addCompareOutput( + \\pub export fn main() void { + \\ var i: u32 = 16; + \\ assert(i << 1, 32); + \\} + \\fn assert(a: u32, b: u32) void { + \\ if (a != b) unreachable; + \\} + , ""); } {