Merge pull request #7507 from joachimschmidt557/stage2-arm

stage2 ARM: implement basic binary bitwise operations
This commit is contained in:
Veikka Tuominen
2020-12-23 12:17:40 +02:00
committed by GitHub
5 changed files with 240 additions and 6 deletions

View File

@@ -764,6 +764,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.arg => return self.genArg(inst.castTag(.arg).?),
.assembly => return self.genAsm(inst.castTag(.assembly).?),
.bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
.bitand => return self.genBitAnd(inst.castTag(.bitand).?),
.bitor => return self.genBitOr(inst.castTag(.bitor).?),
.block => return self.genBlock(inst.castTag(.block).?),
.br => return self.genBr(inst.castTag(.br).?),
.breakpoint => return self.genBreakpoint(inst.src),
@@ -799,6 +801,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
.wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
.varptr => return self.genVarPtr(inst.castTag(.varptr).?),
.xor => return self.genXor(inst.castTag(.xor).?),
}
}
@@ -1009,6 +1012,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
switch (arch) {
.arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bitand),
else => return self.fail(inst.base.src, "TODO implement bitwise and for {}", .{self.target.cpu.arch}),
}
}
fn genBitOr(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
switch (arch) {
.arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bitor),
else => return self.fail(inst.base.src, "TODO implement bitwise or for {}", .{self.target.cpu.arch}),
}
}
fn genXor(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
switch (arch) {
.arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .xor),
else => return self.fail(inst.base.src, "TODO implement xor for {}", .{self.target.cpu.arch}),
}
}
fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
@@ -1251,13 +1284,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, dst_reg, operand).toU32());
}
},
.booland => {
.booland, .bitand => {
writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, dst_reg, operand).toU32());
},
.boolor => {
.boolor, .bitor => {
writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, dst_reg, operand).toU32());
},
.not => {
.not, .xor => {
writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, dst_reg, operand).toU32());
},
else => unreachable, // not a binary instruction

View File

@@ -56,7 +56,9 @@ pub const Inst = struct {
alloc,
arg,
assembly,
bitand,
bitcast,
bitor,
block,
br,
breakpoint,
@@ -93,6 +95,7 @@ pub const Inst = struct {
intcast,
unwrap_optional,
wrap_optional,
xor,
switchbr,
pub fn Type(tag: Tag) type {
@@ -130,6 +133,9 @@ pub const Inst = struct {
.store,
.booland,
.boolor,
.bitand,
.bitor,
.xor,
=> BinOp,
.arg => Arg,

View File

@@ -2342,6 +2342,9 @@ const EmitZIR = struct {
.cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
.booland => try self.emitBinOp(inst.src, new_body, inst.castTag(.booland).?, .booland),
.boolor => try self.emitBinOp(inst.src, new_body, inst.castTag(.boolor).?, .boolor),
.bitand => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitand).?, .bitand),
.bitor => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitor).?, .bitor),
.xor => try self.emitBinOp(inst.src, new_body, inst.castTag(.xor).?, .xor),
.bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast),
.intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),

View File

@@ -1459,7 +1459,66 @@ fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError
}
fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
const tracy = trace(@src());
defer tracy.end();
const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
const instructions = &[_]*Inst{ lhs, rhs };
const resolved_type = try mod.resolvePeerTypes(scope, instructions);
const casted_lhs = try mod.coerce(scope, resolved_type, lhs);
const casted_rhs = try mod.coerce(scope, resolved_type, rhs);
const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
resolved_type.elemType()
else
resolved_type;
const scalar_tag = scalar_type.zigTypeTag();
if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{
lhs.ty.arrayLen(),
rhs.ty.arrayLen(),
});
}
return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBitwise", .{});
} else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
lhs.ty,
rhs.ty,
});
}
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
if (!is_int) {
return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
}
if (casted_lhs.value()) |lhs_val| {
if (casted_rhs.value()) |rhs_val| {
if (lhs_val.isUndef() or rhs_val.isUndef()) {
return mod.constInst(scope, inst.base.src, .{
.ty = resolved_type,
.val = Value.initTag(.undef),
});
}
return mod.fail(scope, inst.base.src, "TODO implement comptime bitwise operations", .{});
}
}
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
const ir_tag = switch (inst.base.tag) {
.bitand => Inst.Tag.bitand,
.bitor => Inst.Tag.bitor,
.xor => Inst.Tag.xor,
else => unreachable,
};
return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
}
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
}
return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{});
} else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
lhs.ty,
rhs.ty,
});

View File

@@ -115,7 +115,8 @@ pub fn addCases(ctx: *TestContext) !void {
}
{
var case = ctx.exe("addition", linux_arm);
var case = ctx.exe("arithmetic operations", linux_arm);
// Add two numbers
case.addCompareOutput(
\\export fn _start() noreturn {
@@ -148,5 +149,137 @@ pub fn addCases(ctx: *TestContext) !void {
,
"12345612345678",
);
// Subtract two numbers
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print(10, 5);
\\ print(4, 3);
\\ exit();
\\}
\\
\\fn print(a: u32, b: u32) void {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (4),
\\ [arg3] "{r2}" (a - b),
\\ [arg1] "{r0}" (1),
\\ [arg2] "{r1}" (@ptrToInt("123456789"))
\\ : "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (1),
\\ [arg1] "{r0}" (0)
\\ : "memory"
\\ );
\\ unreachable;
\\}
,
"123451",
);
// Bitwise And
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print(8, 9);
\\ print(3, 7);
\\ exit();
\\}
\\
\\fn print(a: u32, b: u32) void {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (4),
\\ [arg3] "{r2}" (a & b),
\\ [arg1] "{r0}" (1),
\\ [arg2] "{r1}" (@ptrToInt("123456789"))
\\ : "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (1),
\\ [arg1] "{r0}" (0)
\\ : "memory"
\\ );
\\ unreachable;
\\}
,
"12345678123",
);
// Bitwise Or
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print(4, 2);
\\ print(3, 7);
\\ exit();
\\}
\\
\\fn print(a: u32, b: u32) void {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (4),
\\ [arg3] "{r2}" (a | b),
\\ [arg1] "{r0}" (1),
\\ [arg2] "{r1}" (@ptrToInt("123456789"))
\\ : "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (1),
\\ [arg1] "{r0}" (0)
\\ : "memory"
\\ );
\\ unreachable;
\\}
,
"1234561234567",
);
// Bitwise Xor
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print(42, 42);
\\ print(3, 5);
\\ exit();
\\}
\\
\\fn print(a: u32, b: u32) void {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (4),
\\ [arg3] "{r2}" (a ^ b),
\\ [arg1] "{r0}" (1),
\\ [arg2] "{r1}" (@ptrToInt("123456789"))
\\ : "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (1),
\\ [arg1] "{r0}" (0)
\\ : "memory"
\\ );
\\ unreachable;
\\}
,
"123456",
);
}
}