authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-28 18:33:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-28 18:33:13-07:00
logbbe4a9fa991d1d0756323e67ed546a58d2d2be7e
treef9b4827a1408a156eb65ee82eb9c9f138f8698fa
parent98009a2f66de32d1ea4df41cc03d5ad3d723b3ed

C backend: implement trunc for unsigned non-pow2 ints


3 files changed, 271 insertions(+), 218 deletions(-)

src/codegen/c.zig+45-8
...@@ -501,14 +501,9 @@ pub const DeclGen = struct {...@@ -501,14 +501,9 @@ pub const DeclGen = struct {
501 .signed => "",501 .signed => "",
502 .unsigned => "u",502 .unsigned => "u",
503 };503 };
504 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {504 const c_bits = toCIntBits(info.bits) orelse
505 if (info.bits <= nbits) {
506 try w.print("{s}int{d}_t", .{ sign_prefix, nbits });
507 break;
508 }
509 } else {
510 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});505 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
511 }506 try w.print("{s}int{d}_t", .{ sign_prefix, c_bits });
512 },507 },
513 else => unreachable,508 else => unreachable,
514 }509 }
...@@ -1089,6 +1084,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1089,6 +1084,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1089 .call => try airCall(f, inst),1084 .call => try airCall(f, inst),
1090 .dbg_stmt => try airDbgStmt(f, inst),1085 .dbg_stmt => try airDbgStmt(f, inst),
1091 .intcast => try airIntCast(f, inst),1086 .intcast => try airIntCast(f, inst),
1087 .trunc => try airTrunc(f, inst),
1092 .bool_to_int => try airBoolToInt(f, inst),1088 .bool_to_int => try airBoolToInt(f, inst),
1093 .load => try airLoad(f, inst),1089 .load => try airLoad(f, inst),
1094 .ret => try airRet(f, inst),1090 .ret => try airRet(f, inst),
...@@ -1116,7 +1112,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1116,7 +1112,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1116 .float_to_int,1112 .float_to_int,
1117 .fptrunc,1113 .fptrunc,
1118 .fpext,1114 .fpext,
1119 .trunc,
1120 => try airSimpleCast(f, inst),1115 => try airSimpleCast(f, inst),
11211116
1122 .ptrtoint => try airPtrToInt(f, inst),1117 .ptrtoint => try airPtrToInt(f, inst),
...@@ -1366,6 +1361,39 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1366,6 +1361,39 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
1366 return local;1361 return local;
1367}1362}
13681363
1364fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
1365 if (f.liveness.isUnused(inst)) return CValue.none;
1366
1367 const inst_ty = f.air.typeOfIndex(inst);
1368 const local = try f.allocLocal(inst_ty, .Const);
1369 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1370 const writer = f.object.writer();
1371 const operand = try f.resolveInst(ty_op.operand);
1372 const target = f.object.dg.module.getTarget();
1373 const dest_int_info = inst_ty.intInfo(target);
1374 const dest_bits = dest_int_info.bits;
1375
1376 try writer.writeAll(" = ");
1377
1378 if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) {
1379 try f.writeCValue(writer, operand);
1380 try writer.writeAll(";\n");
1381 return local;
1382 }
1383
1384 switch (dest_int_info.signedness) {
1385 .unsigned => {
1386 try f.writeCValue(writer, operand);
1387 const mask = (@as(u65, 1) << @intCast(u7, dest_bits)) - 1;
1388 try writer.print(" & {d}ULL;\n", .{mask});
1389 return local;
1390 },
1391 .signed => {
1392 return f.fail("TODO: C backend: implement trunc for signed integers", .{});
1393 },
1394 }
1395}
1396
1369fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {1397fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
1370 if (f.liveness.isUnused(inst))1398 if (f.liveness.isUnused(inst))
1371 return CValue.none;1399 return CValue.none;
...@@ -2615,3 +2643,12 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {...@@ -2615,3 +2643,12 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
2615 }2643 }
2616 };2644 };
2617}2645}
2646
2647fn toCIntBits(zig_bits: u32) ?u32 {
2648 for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| {
2649 if (zig_bits <= c_bits) {
2650 return c_bits;
2651 }
2652 }
2653 return null;
2654}
test/behavior/basic.zig+226
...@@ -21,3 +21,229 @@ test "truncate" {...@@ -21,3 +21,229 @@ test "truncate" {
21fn testTruncate(x: u32) u8 {21fn testTruncate(x: u32) u8 {
22 return @truncate(u8, x);22 return @truncate(u8, x);
23}23}
24
25test "truncate to non-power-of-two integers" {
26 try testTrunc(u32, u1, 0b10101, 0b1);
27 try testTrunc(u32, u1, 0b10110, 0b0);
28 try testTrunc(u32, u2, 0b10101, 0b01);
29 try testTrunc(u32, u2, 0b10110, 0b10);
30 // TODO add test coverage for this!
31 // try testTrunc(i32, i3, -4, -4);
32}
33
34fn testTrunc(comptime Big: type, comptime Little: type, big: Big, little: Little) !void {
35 try expect(@truncate(Little, big) == little);
36}
37
38const g1: i32 = 1233 + 1;
39var g2: i32 = 0;
40
41test "global variables" {
42 try expect(g2 == 0);
43 g2 = g1;
44 try expect(g2 == 1234);
45}
46
47test "comptime keyword on expressions" {
48 const x: i32 = comptime x: {
49 break :x 1 + 2 + 3;
50 };
51 try expect(x == comptime 6);
52}
53
54test "type equality" {
55 try expect(*const u8 != *u8);
56}
57
58test "pointer dereferencing" {
59 var x = @as(i32, 3);
60 const y = &x;
61
62 y.* += 1;
63
64 try expect(x == 4);
65 try expect(y.* == 4);
66}
67
68test "const expression eval handling of variables" {
69 var x = true;
70 while (x) {
71 x = false;
72 }
73}
74
75test "character literals" {
76 try expect('\'' == single_quote);
77}
78const single_quote = '\'';
79
80test "non const ptr to aliased type" {
81 const int = i32;
82 try expect(?*int == ?*i32);
83}
84
85test "cold function" {
86 thisIsAColdFn();
87 comptime thisIsAColdFn();
88}
89
90fn thisIsAColdFn() void {
91 @setCold(true);
92}
93
94test "unicode escape in character literal" {
95 var a: u24 = '\u{01f4a9}';
96 try expect(a == 128169);
97}
98
99test "unicode character in character literal" {
100 try expect('💩' == 128169);
101}
102
103fn first4KeysOfHomeRow() []const u8 {
104 return "aoeu";
105}
106
107test "return string from function" {
108 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
109}
110
111test "hex escape" {
112 try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
113}
114
115test "multiline string" {
116 const s1 =
117 \\one
118 \\two)
119 \\three
120 ;
121 const s2 = "one\ntwo)\nthree";
122 try expect(mem.eql(u8, s1, s2));
123}
124
125test "multiline string comments at start" {
126 const s1 =
127 //\\one
128 \\two)
129 \\three
130 ;
131 const s2 = "two)\nthree";
132 try expect(mem.eql(u8, s1, s2));
133}
134
135test "multiline string comments at end" {
136 const s1 =
137 \\one
138 \\two)
139 //\\three
140 ;
141 const s2 = "one\ntwo)";
142 try expect(mem.eql(u8, s1, s2));
143}
144
145test "multiline string comments in middle" {
146 const s1 =
147 \\one
148 //\\two)
149 \\three
150 ;
151 const s2 = "one\nthree";
152 try expect(mem.eql(u8, s1, s2));
153}
154
155test "multiline string comments at multiple places" {
156 const s1 =
157 \\one
158 //\\two
159 \\three
160 //\\four
161 \\five
162 ;
163 const s2 = "one\nthree\nfive";
164 try expect(mem.eql(u8, s1, s2));
165}
166
167test "string concatenation" {
168 try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
169}
170
171test "array mult operator" {
172 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
173}
174
175const OpaqueA = opaque {};
176const OpaqueB = opaque {};
177
178test "opaque types" {
179 try expect(*OpaqueA != *OpaqueB);
180 if (!builtin.zig_is_stage2) {
181 try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));
182 try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
183 }
184}
185
186const global_a: i32 = 1234;
187const global_b: *const i32 = &global_a;
188const global_c: *const f32 = @ptrCast(*const f32, global_b);
189test "compile time global reinterpret" {
190 const d = @ptrCast(*const i32, global_c);
191 try expect(d.* == 1234);
192}
193
194test "cast undefined" {
195 const array: [100]u8 = undefined;
196 const slice = @as([]const u8, &array);
197 testCastUndefined(slice);
198}
199fn testCastUndefined(x: []const u8) void {
200 _ = x;
201}
202
203test "implicit cast after unreachable" {
204 try expect(outer() == 1234);
205}
206fn inner() i32 {
207 return 1234;
208}
209fn outer() i64 {
210 return inner();
211}
212
213test "comptime if inside runtime while which unconditionally breaks" {
214 testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
215 comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
216}
217fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void {
218 while (cond) {
219 if (false) {}
220 break;
221 }
222}
223
224test "implicit comptime while" {
225 while (false) {
226 @compileError("bad");
227 }
228}
229
230fn fnThatClosesOverLocalConst() type {
231 const c = 1;
232 return struct {
233 fn g() i32 {
234 return c;
235 }
236 };
237}
238
239test "function closes over local const" {
240 const x = fnThatClosesOverLocalConst().g();
241 try expect(x == 1);
242}
243
244test "volatile load and store" {
245 var number: i32 = 1234;
246 const ptr = @as(*volatile i32, &number);
247 ptr.* += 1;
248 try expect(ptr.* == 1235);
249}
test/behavior/basic_llvm.zig-210
...@@ -4,135 +4,6 @@ const mem = std.mem;...@@ -4,135 +4,6 @@ const mem = std.mem;
4const expect = std.testing.expect;4const expect = std.testing.expect;
5const expectEqualStrings = std.testing.expectEqualStrings;5const expectEqualStrings = std.testing.expectEqualStrings;
66
7const g1: i32 = 1233 + 1;
8var g2: i32 = 0;
9
10test "global variables" {
11 try expect(g2 == 0);
12 g2 = g1;
13 try expect(g2 == 1234);
14}
15
16test "comptime keyword on expressions" {
17 const x: i32 = comptime x: {
18 break :x 1 + 2 + 3;
19 };
20 try expect(x == comptime 6);
21}
22
23test "type equality" {
24 try expect(*const u8 != *u8);
25}
26
27test "pointer dereferencing" {
28 var x = @as(i32, 3);
29 const y = &x;
30
31 y.* += 1;
32
33 try expect(x == 4);
34 try expect(y.* == 4);
35}
36
37test "const expression eval handling of variables" {
38 var x = true;
39 while (x) {
40 x = false;
41 }
42}
43
44test "character literals" {
45 try expect('\'' == single_quote);
46}
47const single_quote = '\'';
48
49test "non const ptr to aliased type" {
50 const int = i32;
51 try expect(?*int == ?*i32);
52}
53
54test "cold function" {
55 thisIsAColdFn();
56 comptime thisIsAColdFn();
57}
58
59fn thisIsAColdFn() void {
60 @setCold(true);
61}
62
63test "unicode escape in character literal" {
64 var a: u24 = '\u{01f4a9}';
65 try expect(a == 128169);
66}
67
68test "unicode character in character literal" {
69 try expect('💩' == 128169);
70}
71
72fn first4KeysOfHomeRow() []const u8 {
73 return "aoeu";
74}
75
76test "return string from function" {
77 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
78}
79
80test "hex escape" {
81 try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
82}
83
84test "multiline string" {
85 const s1 =
86 \\one
87 \\two)
88 \\three
89 ;
90 const s2 = "one\ntwo)\nthree";
91 try expect(mem.eql(u8, s1, s2));
92}
93
94test "multiline string comments at start" {
95 const s1 =
96 //\\one
97 \\two)
98 \\three
99 ;
100 const s2 = "two)\nthree";
101 try expect(mem.eql(u8, s1, s2));
102}
103
104test "multiline string comments at end" {
105 const s1 =
106 \\one
107 \\two)
108 //\\three
109 ;
110 const s2 = "one\ntwo)";
111 try expect(mem.eql(u8, s1, s2));
112}
113
114test "multiline string comments in middle" {
115 const s1 =
116 \\one
117 //\\two)
118 \\three
119 ;
120 const s2 = "one\nthree";
121 try expect(mem.eql(u8, s1, s2));
122}
123
124test "multiline string comments at multiple places" {
125 const s1 =
126 \\one
127 //\\two
128 \\three
129 //\\four
130 \\five
131 ;
132 const s2 = "one\nthree\nfive";
133 try expect(mem.eql(u8, s1, s2));
134}
135
136test "call result of if else expression" {7test "call result of if else expression" {
137 try expect(mem.eql(u8, f2(true), "a"));8 try expect(mem.eql(u8, f2(true), "a"));
138 try expect(mem.eql(u8, f2(false), "b"));9 try expect(mem.eql(u8, f2(false), "b"));
...@@ -147,14 +18,6 @@ fn fB() []const u8 {...@@ -147,14 +18,6 @@ fn fB() []const u8 {
147 return "b";18 return "b";
148}19}
14920
150test "string concatenation" {
151 try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
152}
153
154test "array mult operator" {
155 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
156}
157
158test "memcpy and memset intrinsics" {21test "memcpy and memset intrinsics" {
159 try testMemcpyMemset();22 try testMemcpyMemset();
160 // TODO add comptime test coverage23 // TODO add comptime test coverage
...@@ -176,14 +39,6 @@ fn testMemcpyMemset() !void {...@@ -176,14 +39,6 @@ fn testMemcpyMemset() !void {
176const OpaqueA = opaque {};39const OpaqueA = opaque {};
177const OpaqueB = opaque {};40const OpaqueB = opaque {};
17841
179test "opaque types" {
180 try expect(*OpaqueA != *OpaqueB);
181 if (!builtin.zig_is_stage2) {
182 try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));
183 try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
184 }
185}
186
187test "variable is allowed to be a pointer to an opaque type" {42test "variable is allowed to be a pointer to an opaque type" {
188 var x: i32 = 1234;43 var x: i32 = 1234;
189 _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));44 _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));
...@@ -193,33 +48,6 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA {...@@ -193,33 +48,6 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA {
193 return a;48 return a;
194}49}
19550
196const global_a: i32 = 1234;
197const global_b: *const i32 = &global_a;
198const global_c: *const f32 = @ptrCast(*const f32, global_b);
199test "compile time global reinterpret" {
200 const d = @ptrCast(*const i32, global_c);
201 try expect(d.* == 1234);
202}
203
204test "cast undefined" {
205 const array: [100]u8 = undefined;
206 const slice = @as([]const u8, &array);
207 testCastUndefined(slice);
208}
209fn testCastUndefined(x: []const u8) void {
210 _ = x;
211}
212
213test "implicit cast after unreachable" {
214 try expect(outer() == 1234);
215}
216fn inner() i32 {
217 return 1234;
218}
219fn outer() i64 {
220 return inner();
221}
222
223test "take address of parameter" {51test "take address of parameter" {
224 try testTakeAddressOfParameter(12.34);52 try testTakeAddressOfParameter(12.34);
225}53}
...@@ -262,44 +90,6 @@ fn nine() u8 {...@@ -262,44 +90,6 @@ fn nine() u8 {
262 return 9;90 return 9;
263}91}
26492
265test "comptime if inside runtime while which unconditionally breaks" {
266 testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
267 comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
268}
269fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void {
270 while (cond) {
271 if (false) {}
272 break;
273 }
274}
275
276test "implicit comptime while" {
277 while (false) {
278 @compileError("bad");
279 }
280}
281
282fn fnThatClosesOverLocalConst() type {
283 const c = 1;
284 return struct {
285 fn g() i32 {
286 return c;
287 }
288 };
289}
290
291test "function closes over local const" {
292 const x = fnThatClosesOverLocalConst().g();
293 try expect(x == 1);
294}
295
296test "volatile load and store" {
297 var number: i32 = 1234;
298 const ptr = @as(*volatile i32, &number);
299 ptr.* += 1;
300 try expect(ptr.* == 1235);
301}
302
303test "struct inside function" {93test "struct inside function" {
304 try testStructInFn();94 try testStructInFn();
305 comptime try testStructInFn();95 comptime try testStructInFn();