Merge pull request #9587 from g-w1/sh

stage2: implement shr and shl
This commit is contained in:
Andrew Kelley 2021-08-19 23:47:44 -04:00 committed by GitHub
commit 6a5094872f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 198 additions and 7 deletions

View File

@ -94,6 +94,12 @@ pub const Inst = struct {
/// Result type is the same as both operands. /// Result type is the same as both operands.
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
bit_or, bit_or,
/// Shift right. `>>`
/// Uses the `bin_op` field.
shr,
/// Shift left. `<<`
/// Uses the `bin_op` field.
shl,
/// Bitwise XOR. `^` /// Bitwise XOR. `^`
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
xor, xor,
@ -445,6 +451,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.xor, .xor,
.ptr_add, .ptr_add,
.ptr_sub, .ptr_sub,
.shr,
.shl,
=> return air.typeOf(datas[inst].bin_op.lhs), => return air.typeOf(datas[inst].bin_op.lhs),
.cmp_lt, .cmp_lt,

View File

@ -249,6 +249,8 @@ fn analyzeInst(
.ptr_slice_elem_val, .ptr_slice_elem_val,
.ptr_elem_val, .ptr_elem_val,
.ptr_ptr_elem_val, .ptr_ptr_elem_val,
.shl,
.shr,
=> { => {
const o = inst_datas[inst].bin_op; const o = inst_datas[inst].bin_op;
return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });

View File

@ -5294,17 +5294,50 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
_ = block; const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
_ = inst; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{}); 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 { fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
_ = inst; const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{}); 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( fn zirBitwise(
@ -6001,13 +6034,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 { 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 inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src(); 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 { 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 inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src(); 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( fn zirTypeofPeer(

View File

@ -822,6 +822,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.bit_and => try self.airBitAnd(inst), .bit_and => try self.airBitAnd(inst),
.bit_or => try self.airBitOr(inst), .bit_or => try self.airBitOr(inst),
.xor => try self.airXor(inst), .xor => try self.airXor(inst),
.shr => try self.airShr(inst),
.shl => try self.airShl(inst),
.alloc => try self.airAlloc(inst), .alloc => try self.airAlloc(inst),
.arg => try self.airArg(inst), .arg => try self.airArg(inst),
@ -1272,6 +1274,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 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 { fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op; const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {

View File

@ -871,6 +871,9 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
.bit_or => try airBinOp(o, inst, " | "), .bit_or => try airBinOp(o, inst, " | "),
.xor => try airBinOp(o, inst, " ^ "), .xor => try airBinOp(o, inst, " ^ "),
.shr => try airBinOp(o, inst, " >> "),
.shl => try airBinOp(o, inst, " << "),
.not => try airNot( o, inst), .not => try airNot( o, inst),
.optional_payload => try airOptionalPayload(o, inst), .optional_payload => try airOptionalPayload(o, inst),

View File

@ -993,6 +993,9 @@ pub const FuncGen = struct {
.bit_or, .bool_or => try self.airOr(inst), .bit_or, .bool_or => try self.airOr(inst),
.xor => try self.airXor(inst), .xor => try self.airXor(inst),
.shl => try self.airShl(inst),
.shr => try self.airShr(inst),
.cmp_eq => try self.airCmp(inst, .eq), .cmp_eq => try self.airCmp(inst, .eq),
.cmp_gt => try self.airCmp(inst, .gt), .cmp_gt => try self.airCmp(inst, .gt),
.cmp_gte => try self.airCmp(inst, .gte), .cmp_gte => try self.airCmp(inst, .gte),
@ -1736,6 +1739,41 @@ pub const FuncGen = struct {
return self.builder.buildXor(lhs, rhs, ""); 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);
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 {
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 { fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) if (self.liveness.isUnused(inst))
return null; return null;

View File

@ -290,6 +290,14 @@ pub const Builder = opaque {
pub const getInsertBlock = LLVMGetInsertBlock; pub const getInsertBlock = LLVMGetInsertBlock;
extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock; 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; pub const buildCall = LLVMBuildCall;
extern fn LLVMBuildCall( extern fn LLVMBuildCall(
*const Builder, *const Builder,
@ -381,6 +389,15 @@ pub const Builder = opaque {
pub const buildAnd = LLVMBuildAnd; pub const buildAnd = LLVMBuildAnd;
extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; 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; pub const buildOr = LLVMBuildOr;
extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;

View File

@ -127,6 +127,8 @@ const Writer = struct {
.ptr_slice_elem_val, .ptr_slice_elem_val,
.ptr_elem_val, .ptr_elem_val,
.ptr_ptr_elem_val, .ptr_ptr_elem_val,
.shl,
.shr,
=> try w.writeBinOp(s, inst), => try w.writeBinOp(s, inst),
.is_null, .is_null,

View File

@ -808,6 +808,31 @@ pub fn addCases(ctx: *TestContext) !void {
}); });
} }
{
var case = ctx.exeFromCompiledC("shift right + left", .{});
case.addCompareOutput(
\\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;
\\}
, "");
}
{ {
var case = ctx.exeFromCompiledC("inferred error sets", .{}); var case = ctx.exeFromCompiledC("inferred error sets", .{});

View File

@ -28,6 +28,29 @@ pub fn addCases(ctx: *TestContext) !void {
, ""); , "");
} }
{
var case = ctx.exeUsingLlvmBackend("shift right + left", 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;
\\}
, "");
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;
\\}
, "");
}
{ {
var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64); var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64);