diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-10-28 18:33:13 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-10-28 18:33:13 -0700 |
| commit | bbe4a9fa991d1d0756323e67ed546a58d2d2be7e (patch) | |
| tree | f9b4827a1408a156eb65ee82eb9c9f138f8698fa | |
| parent | 98009a2f66de32d1ea4df41cc03d5ad3d723b3ed (diff) | |
| download | zig-bbe4a9fa991d1d0756323e67ed546a58d2d2be7e.tar.gz zig-bbe4a9fa991d1d0756323e67ed546a58d2d2be7e.zip | |
C backend: implement trunc for unsigned non-pow2 ints
| -rw-r--r-- | src/codegen/c.zig | 53 | ||||
| -rw-r--r-- | test/behavior/basic.zig | 226 | ||||
| -rw-r--r-- | test/behavior/basic_llvm.zig | 210 |
3 files changed, 271 insertions, 218 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 444adf0aa1..3ab74a85be 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -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; +} diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 691e88256c..72babc8118 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -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); +} diff --git a/test/behavior/basic_llvm.zig b/test/behavior/basic_llvm.zig index 885f69f8bc..32b35bef0a 100644 --- a/test/behavior/basic_llvm.zig +++ b/test/behavior/basic_llvm.zig @@ -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(); |
