mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
C backend: implement trunc for unsigned non-pow2 ints
This commit is contained in:
parent
98009a2f66
commit
bbe4a9fa99
@ -501,14 +501,9 @@ pub const DeclGen = struct {
|
||||
.signed => "",
|
||||
.unsigned => "u",
|
||||
};
|
||||
inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {
|
||||
if (info.bits <= nbits) {
|
||||
try w.print("{s}int{d}_t", .{ sign_prefix, nbits });
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const c_bits = toCIntBits(info.bits) orelse
|
||||
return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
|
||||
}
|
||||
try w.print("{s}int{d}_t", .{ sign_prefix, c_bits });
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1089,6 +1084,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||
.call => try airCall(f, inst),
|
||||
.dbg_stmt => try airDbgStmt(f, inst),
|
||||
.intcast => try airIntCast(f, inst),
|
||||
.trunc => try airTrunc(f, inst),
|
||||
.bool_to_int => try airBoolToInt(f, inst),
|
||||
.load => try airLoad(f, inst),
|
||||
.ret => try airRet(f, inst),
|
||||
@ -1116,7 +1112,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||
.float_to_int,
|
||||
.fptrunc,
|
||||
.fpext,
|
||||
.trunc,
|
||||
=> try airSimpleCast(f, inst),
|
||||
|
||||
.ptrtoint => try airPtrToInt(f, inst),
|
||||
@ -1366,6 +1361,39 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||
|
||||
const inst_ty = f.air.typeOfIndex(inst);
|
||||
const local = try f.allocLocal(inst_ty, .Const);
|
||||
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
|
||||
const writer = f.object.writer();
|
||||
const operand = try f.resolveInst(ty_op.operand);
|
||||
const target = f.object.dg.module.getTarget();
|
||||
const dest_int_info = inst_ty.intInfo(target);
|
||||
const dest_bits = dest_int_info.bits;
|
||||
|
||||
try writer.writeAll(" = ");
|
||||
|
||||
if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) {
|
||||
try f.writeCValue(writer, operand);
|
||||
try writer.writeAll(";\n");
|
||||
return local;
|
||||
}
|
||||
|
||||
switch (dest_int_info.signedness) {
|
||||
.unsigned => {
|
||||
try f.writeCValue(writer, operand);
|
||||
const mask = (@as(u65, 1) << @intCast(u7, dest_bits)) - 1;
|
||||
try writer.print(" & {d}ULL;\n", .{mask});
|
||||
return local;
|
||||
},
|
||||
.signed => {
|
||||
return f.fail("TODO: C backend: implement trunc for signed integers", .{});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
if (f.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
@ -2615,3 +2643,12 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn toCIntBits(zig_bits: u32) ?u32 {
|
||||
for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| {
|
||||
if (zig_bits <= c_bits) {
|
||||
return c_bits;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -21,3 +21,229 @@ test "truncate" {
|
||||
fn testTruncate(x: u32) u8 {
|
||||
return @truncate(u8, x);
|
||||
}
|
||||
|
||||
test "truncate to non-power-of-two integers" {
|
||||
try testTrunc(u32, u1, 0b10101, 0b1);
|
||||
try testTrunc(u32, u1, 0b10110, 0b0);
|
||||
try testTrunc(u32, u2, 0b10101, 0b01);
|
||||
try testTrunc(u32, u2, 0b10110, 0b10);
|
||||
// TODO add test coverage for this!
|
||||
// try testTrunc(i32, i3, -4, -4);
|
||||
}
|
||||
|
||||
fn testTrunc(comptime Big: type, comptime Little: type, big: Big, little: Little) !void {
|
||||
try expect(@truncate(Little, big) == little);
|
||||
}
|
||||
|
||||
const g1: i32 = 1233 + 1;
|
||||
var g2: i32 = 0;
|
||||
|
||||
test "global variables" {
|
||||
try expect(g2 == 0);
|
||||
g2 = g1;
|
||||
try expect(g2 == 1234);
|
||||
}
|
||||
|
||||
test "comptime keyword on expressions" {
|
||||
const x: i32 = comptime x: {
|
||||
break :x 1 + 2 + 3;
|
||||
};
|
||||
try expect(x == comptime 6);
|
||||
}
|
||||
|
||||
test "type equality" {
|
||||
try expect(*const u8 != *u8);
|
||||
}
|
||||
|
||||
test "pointer dereferencing" {
|
||||
var x = @as(i32, 3);
|
||||
const y = &x;
|
||||
|
||||
y.* += 1;
|
||||
|
||||
try expect(x == 4);
|
||||
try expect(y.* == 4);
|
||||
}
|
||||
|
||||
test "const expression eval handling of variables" {
|
||||
var x = true;
|
||||
while (x) {
|
||||
x = false;
|
||||
}
|
||||
}
|
||||
|
||||
test "character literals" {
|
||||
try expect('\'' == single_quote);
|
||||
}
|
||||
const single_quote = '\'';
|
||||
|
||||
test "non const ptr to aliased type" {
|
||||
const int = i32;
|
||||
try expect(?*int == ?*i32);
|
||||
}
|
||||
|
||||
test "cold function" {
|
||||
thisIsAColdFn();
|
||||
comptime thisIsAColdFn();
|
||||
}
|
||||
|
||||
fn thisIsAColdFn() void {
|
||||
@setCold(true);
|
||||
}
|
||||
|
||||
test "unicode escape in character literal" {
|
||||
var a: u24 = '\u{01f4a9}';
|
||||
try expect(a == 128169);
|
||||
}
|
||||
|
||||
test "unicode character in character literal" {
|
||||
try expect('💩' == 128169);
|
||||
}
|
||||
|
||||
fn first4KeysOfHomeRow() []const u8 {
|
||||
return "aoeu";
|
||||
}
|
||||
|
||||
test "return string from function" {
|
||||
try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
|
||||
}
|
||||
|
||||
test "hex escape" {
|
||||
try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
|
||||
}
|
||||
|
||||
test "multiline string" {
|
||||
const s1 =
|
||||
\\one
|
||||
\\two)
|
||||
\\three
|
||||
;
|
||||
const s2 = "one\ntwo)\nthree";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments at start" {
|
||||
const s1 =
|
||||
//\\one
|
||||
\\two)
|
||||
\\three
|
||||
;
|
||||
const s2 = "two)\nthree";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments at end" {
|
||||
const s1 =
|
||||
\\one
|
||||
\\two)
|
||||
//\\three
|
||||
;
|
||||
const s2 = "one\ntwo)";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments in middle" {
|
||||
const s1 =
|
||||
\\one
|
||||
//\\two)
|
||||
\\three
|
||||
;
|
||||
const s2 = "one\nthree";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments at multiple places" {
|
||||
const s1 =
|
||||
\\one
|
||||
//\\two
|
||||
\\three
|
||||
//\\four
|
||||
\\five
|
||||
;
|
||||
const s2 = "one\nthree\nfive";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "string concatenation" {
|
||||
try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
|
||||
}
|
||||
|
||||
test "array mult operator" {
|
||||
try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
|
||||
}
|
||||
|
||||
const OpaqueA = opaque {};
|
||||
const OpaqueB = opaque {};
|
||||
|
||||
test "opaque types" {
|
||||
try expect(*OpaqueA != *OpaqueB);
|
||||
if (!builtin.zig_is_stage2) {
|
||||
try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));
|
||||
try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
|
||||
}
|
||||
}
|
||||
|
||||
const global_a: i32 = 1234;
|
||||
const global_b: *const i32 = &global_a;
|
||||
const global_c: *const f32 = @ptrCast(*const f32, global_b);
|
||||
test "compile time global reinterpret" {
|
||||
const d = @ptrCast(*const i32, global_c);
|
||||
try expect(d.* == 1234);
|
||||
}
|
||||
|
||||
test "cast undefined" {
|
||||
const array: [100]u8 = undefined;
|
||||
const slice = @as([]const u8, &array);
|
||||
testCastUndefined(slice);
|
||||
}
|
||||
fn testCastUndefined(x: []const u8) void {
|
||||
_ = x;
|
||||
}
|
||||
|
||||
test "implicit cast after unreachable" {
|
||||
try expect(outer() == 1234);
|
||||
}
|
||||
fn inner() i32 {
|
||||
return 1234;
|
||||
}
|
||||
fn outer() i64 {
|
||||
return inner();
|
||||
}
|
||||
|
||||
test "comptime if inside runtime while which unconditionally breaks" {
|
||||
testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
|
||||
comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
|
||||
}
|
||||
fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void {
|
||||
while (cond) {
|
||||
if (false) {}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit comptime while" {
|
||||
while (false) {
|
||||
@compileError("bad");
|
||||
}
|
||||
}
|
||||
|
||||
fn fnThatClosesOverLocalConst() type {
|
||||
const c = 1;
|
||||
return struct {
|
||||
fn g() i32 {
|
||||
return c;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "function closes over local const" {
|
||||
const x = fnThatClosesOverLocalConst().g();
|
||||
try expect(x == 1);
|
||||
}
|
||||
|
||||
test "volatile load and store" {
|
||||
var number: i32 = 1234;
|
||||
const ptr = @as(*volatile i32, &number);
|
||||
ptr.* += 1;
|
||||
try expect(ptr.* == 1235);
|
||||
}
|
||||
|
||||
@ -4,135 +4,6 @@ const mem = std.mem;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqualStrings = std.testing.expectEqualStrings;
|
||||
|
||||
const g1: i32 = 1233 + 1;
|
||||
var g2: i32 = 0;
|
||||
|
||||
test "global variables" {
|
||||
try expect(g2 == 0);
|
||||
g2 = g1;
|
||||
try expect(g2 == 1234);
|
||||
}
|
||||
|
||||
test "comptime keyword on expressions" {
|
||||
const x: i32 = comptime x: {
|
||||
break :x 1 + 2 + 3;
|
||||
};
|
||||
try expect(x == comptime 6);
|
||||
}
|
||||
|
||||
test "type equality" {
|
||||
try expect(*const u8 != *u8);
|
||||
}
|
||||
|
||||
test "pointer dereferencing" {
|
||||
var x = @as(i32, 3);
|
||||
const y = &x;
|
||||
|
||||
y.* += 1;
|
||||
|
||||
try expect(x == 4);
|
||||
try expect(y.* == 4);
|
||||
}
|
||||
|
||||
test "const expression eval handling of variables" {
|
||||
var x = true;
|
||||
while (x) {
|
||||
x = false;
|
||||
}
|
||||
}
|
||||
|
||||
test "character literals" {
|
||||
try expect('\'' == single_quote);
|
||||
}
|
||||
const single_quote = '\'';
|
||||
|
||||
test "non const ptr to aliased type" {
|
||||
const int = i32;
|
||||
try expect(?*int == ?*i32);
|
||||
}
|
||||
|
||||
test "cold function" {
|
||||
thisIsAColdFn();
|
||||
comptime thisIsAColdFn();
|
||||
}
|
||||
|
||||
fn thisIsAColdFn() void {
|
||||
@setCold(true);
|
||||
}
|
||||
|
||||
test "unicode escape in character literal" {
|
||||
var a: u24 = '\u{01f4a9}';
|
||||
try expect(a == 128169);
|
||||
}
|
||||
|
||||
test "unicode character in character literal" {
|
||||
try expect('💩' == 128169);
|
||||
}
|
||||
|
||||
fn first4KeysOfHomeRow() []const u8 {
|
||||
return "aoeu";
|
||||
}
|
||||
|
||||
test "return string from function" {
|
||||
try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
|
||||
}
|
||||
|
||||
test "hex escape" {
|
||||
try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
|
||||
}
|
||||
|
||||
test "multiline string" {
|
||||
const s1 =
|
||||
\\one
|
||||
\\two)
|
||||
\\three
|
||||
;
|
||||
const s2 = "one\ntwo)\nthree";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments at start" {
|
||||
const s1 =
|
||||
//\\one
|
||||
\\two)
|
||||
\\three
|
||||
;
|
||||
const s2 = "two)\nthree";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments at end" {
|
||||
const s1 =
|
||||
\\one
|
||||
\\two)
|
||||
//\\three
|
||||
;
|
||||
const s2 = "one\ntwo)";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments in middle" {
|
||||
const s1 =
|
||||
\\one
|
||||
//\\two)
|
||||
\\three
|
||||
;
|
||||
const s2 = "one\nthree";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "multiline string comments at multiple places" {
|
||||
const s1 =
|
||||
\\one
|
||||
//\\two
|
||||
\\three
|
||||
//\\four
|
||||
\\five
|
||||
;
|
||||
const s2 = "one\nthree\nfive";
|
||||
try expect(mem.eql(u8, s1, s2));
|
||||
}
|
||||
|
||||
test "call result of if else expression" {
|
||||
try expect(mem.eql(u8, f2(true), "a"));
|
||||
try expect(mem.eql(u8, f2(false), "b"));
|
||||
@ -147,14 +18,6 @@ fn fB() []const u8 {
|
||||
return "b";
|
||||
}
|
||||
|
||||
test "string concatenation" {
|
||||
try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
|
||||
}
|
||||
|
||||
test "array mult operator" {
|
||||
try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
|
||||
}
|
||||
|
||||
test "memcpy and memset intrinsics" {
|
||||
try testMemcpyMemset();
|
||||
// TODO add comptime test coverage
|
||||
@ -176,14 +39,6 @@ fn testMemcpyMemset() !void {
|
||||
const OpaqueA = opaque {};
|
||||
const OpaqueB = opaque {};
|
||||
|
||||
test "opaque types" {
|
||||
try expect(*OpaqueA != *OpaqueB);
|
||||
if (!builtin.zig_is_stage2) {
|
||||
try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));
|
||||
try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
|
||||
}
|
||||
}
|
||||
|
||||
test "variable is allowed to be a pointer to an opaque type" {
|
||||
var x: i32 = 1234;
|
||||
_ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));
|
||||
@ -193,33 +48,6 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA {
|
||||
return a;
|
||||
}
|
||||
|
||||
const global_a: i32 = 1234;
|
||||
const global_b: *const i32 = &global_a;
|
||||
const global_c: *const f32 = @ptrCast(*const f32, global_b);
|
||||
test "compile time global reinterpret" {
|
||||
const d = @ptrCast(*const i32, global_c);
|
||||
try expect(d.* == 1234);
|
||||
}
|
||||
|
||||
test "cast undefined" {
|
||||
const array: [100]u8 = undefined;
|
||||
const slice = @as([]const u8, &array);
|
||||
testCastUndefined(slice);
|
||||
}
|
||||
fn testCastUndefined(x: []const u8) void {
|
||||
_ = x;
|
||||
}
|
||||
|
||||
test "implicit cast after unreachable" {
|
||||
try expect(outer() == 1234);
|
||||
}
|
||||
fn inner() i32 {
|
||||
return 1234;
|
||||
}
|
||||
fn outer() i64 {
|
||||
return inner();
|
||||
}
|
||||
|
||||
test "take address of parameter" {
|
||||
try testTakeAddressOfParameter(12.34);
|
||||
}
|
||||
@ -262,44 +90,6 @@ fn nine() u8 {
|
||||
return 9;
|
||||
}
|
||||
|
||||
test "comptime if inside runtime while which unconditionally breaks" {
|
||||
testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
|
||||
comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
|
||||
}
|
||||
fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void {
|
||||
while (cond) {
|
||||
if (false) {}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit comptime while" {
|
||||
while (false) {
|
||||
@compileError("bad");
|
||||
}
|
||||
}
|
||||
|
||||
fn fnThatClosesOverLocalConst() type {
|
||||
const c = 1;
|
||||
return struct {
|
||||
fn g() i32 {
|
||||
return c;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "function closes over local const" {
|
||||
const x = fnThatClosesOverLocalConst().g();
|
||||
try expect(x == 1);
|
||||
}
|
||||
|
||||
test "volatile load and store" {
|
||||
var number: i32 = 1234;
|
||||
const ptr = @as(*volatile i32, &number);
|
||||
ptr.* += 1;
|
||||
try expect(ptr.* == 1235);
|
||||
}
|
||||
|
||||
test "struct inside function" {
|
||||
try testStructInFn();
|
||||
comptime try testStructInFn();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user