authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 19:48:59-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 19:48:59-04:00
log2cdbb5f47260d2510ce478c77fa9c3c7c29fb671
tree6bebbf19524eaad49c86dbfd8cbc43e2e27375bb
parent0746028a2a6caf28e396f042be0b23c8d6fb7a5e

ir: analyze int casting


8 files changed, 303 insertions(+), 174 deletions(-)

lib/std/math/big/int.zig+1-1
......@@ -237,7 +237,7 @@ pub const Int = struct {
237237 return bits;
238238 }
239239
240 fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
240 pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
241241 if (self.eqZero()) {
242242 return true;
243243 }
lib/std/target.zig+1-1
......@@ -761,7 +761,7 @@ pub const Target = struct {
761761 };
762762 }
763763
764 pub fn ptrBitWidth(arch: Arch) u32 {
764 pub fn ptrBitWidth(arch: Arch) u16 {
765765 switch (arch) {
766766 .avr,
767767 .msp430,
src-self-hosted/c_int.zig deleted-169
......@@ -1,169 +0,0 @@
1const Target = @import("std").Target;
2
3pub const CInt = struct {
4 id: Id,
5 zig_name: []const u8,
6 c_name: []const u8,
7 is_signed: bool,
8
9 pub const Id = enum {
10 Short,
11 UShort,
12 Int,
13 UInt,
14 Long,
15 ULong,
16 LongLong,
17 ULongLong,
18 };
19
20 pub const list = [_]CInt{
21 CInt{
22 .id = .Short,
23 .zig_name = "c_short",
24 .c_name = "short",
25 .is_signed = true,
26 },
27 CInt{
28 .id = .UShort,
29 .zig_name = "c_ushort",
30 .c_name = "unsigned short",
31 .is_signed = false,
32 },
33 CInt{
34 .id = .Int,
35 .zig_name = "c_int",
36 .c_name = "int",
37 .is_signed = true,
38 },
39 CInt{
40 .id = .UInt,
41 .zig_name = "c_uint",
42 .c_name = "unsigned int",
43 .is_signed = false,
44 },
45 CInt{
46 .id = .Long,
47 .zig_name = "c_long",
48 .c_name = "long",
49 .is_signed = true,
50 },
51 CInt{
52 .id = .ULong,
53 .zig_name = "c_ulong",
54 .c_name = "unsigned long",
55 .is_signed = false,
56 },
57 CInt{
58 .id = .LongLong,
59 .zig_name = "c_longlong",
60 .c_name = "long long",
61 .is_signed = true,
62 },
63 CInt{
64 .id = .ULongLong,
65 .zig_name = "c_ulonglong",
66 .c_name = "unsigned long long",
67 .is_signed = false,
68 },
69 };
70
71 pub fn sizeInBits(cint: CInt, self: Target) u32 {
72 const arch = self.cpu.arch;
73 switch (self.os.tag) {
74 .freestanding, .other => switch (self.cpu.arch) {
75 .msp430 => switch (cint.id) {
76 .Short,
77 .UShort,
78 .Int,
79 .UInt,
80 => return 16,
81 .Long,
82 .ULong,
83 => return 32,
84 .LongLong,
85 .ULongLong,
86 => return 64,
87 },
88 else => switch (cint.id) {
89 .Short,
90 .UShort,
91 => return 16,
92 .Int,
93 .UInt,
94 => return 32,
95 .Long,
96 .ULong,
97 => return self.cpu.arch.ptrBitWidth(),
98 .LongLong,
99 .ULongLong,
100 => return 64,
101 },
102 },
103
104 .linux,
105 .macosx,
106 .freebsd,
107 .openbsd,
108 => switch (cint.id) {
109 .Short,
110 .UShort,
111 => return 16,
112 .Int,
113 .UInt,
114 => return 32,
115 .Long,
116 .ULong,
117 => return self.cpu.arch.ptrBitWidth(),
118 .LongLong,
119 .ULongLong,
120 => return 64,
121 },
122
123 .windows, .uefi => switch (cint.id) {
124 .Short,
125 .UShort,
126 => return 16,
127 .Int,
128 .UInt,
129 => return 32,
130 .Long,
131 .ULong,
132 .LongLong,
133 .ULongLong,
134 => return 64,
135 },
136
137 .ananas,
138 .cloudabi,
139 .dragonfly,
140 .fuchsia,
141 .ios,
142 .kfreebsd,
143 .lv2,
144 .netbsd,
145 .solaris,
146 .haiku,
147 .minix,
148 .rtems,
149 .nacl,
150 .cnk,
151 .aix,
152 .cuda,
153 .nvcl,
154 .amdhsa,
155 .ps4,
156 .elfiamcu,
157 .tvos,
158 .watchos,
159 .mesa3d,
160 .contiki,
161 .amdpal,
162 .hermit,
163 .hurd,
164 .wasi,
165 .emscripten,
166 => @panic("TODO specify the C integer type sizes for this OS"),
167 }
168 }
169};
src-self-hosted/ir.zig+49
......@@ -6,6 +6,7 @@ const Type = @import("type.zig").Type;
66const assert = std.debug.assert;
77const text = @import("ir/text.zig");
88const BigInt = std.math.big.Int;
9const Target = std.Target;
910
1011/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
1112/// of instructions that correspond to the ZIR text format.
......@@ -99,6 +100,8 @@ pub const ErrorMsg = struct {
99100};
100101
101102pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
103 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
104
102105 var ctx = Analyze{
103106 .allocator = allocator,
104107 .arena = std.heap.ArenaAllocator.init(allocator),
......@@ -107,6 +110,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
107110 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
108111 .exports = std.ArrayList(Module.Export).init(allocator),
109112 .fns = std.ArrayList(Module.Fn).init(allocator),
113 .target = native_info.target,
110114 };
111115 defer ctx.errors.deinit();
112116 defer ctx.decl_table.deinit();
......@@ -135,6 +139,7 @@ const Analyze = struct {
135139 decl_table: std.AutoHashMap(*text.Inst, NewDecl),
136140 exports: std.ArrayList(Module.Export),
137141 fns: std.ArrayList(Module.Fn),
142 target: Target,
138143
139144 const NewDecl = struct {
140145 /// null means a semantic analysis error happened
......@@ -336,6 +341,7 @@ const Analyze = struct {
336341 .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
337342 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
338343 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
344 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
339345 }
340346 }
341347
......@@ -402,6 +408,38 @@ const Analyze = struct {
402408 return self.coerce(dest_type, new_inst);
403409 }
404410
411 fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {
412 const dest_type = try self.resolveType(func, intcast.positionals.dest_type);
413 const new_inst = try self.resolveInst(func, intcast.positionals.value);
414
415 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
416 .ComptimeInt => true,
417 .Int => false,
418 else => return self.fail(
419 intcast.positionals.dest_type.src,
420 "expected integer type, found '{}'",
421 .{
422 dest_type,
423 },
424 ),
425 };
426
427 switch (new_inst.ty.zigTypeTag()) {
428 .ComptimeInt, .Int => {},
429 else => return self.fail(
430 intcast.positionals.value.src,
431 "expected integer type, found '{}'",
432 .{new_inst.ty},
433 ),
434 }
435
436 if (dest_is_comptime_int or new_inst.value() != null) {
437 return self.coerce(dest_type, new_inst);
438 }
439
440 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
441 }
442
405443 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
406444 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
407445 if (in_memory_result == .ok) {
......@@ -420,6 +458,17 @@ const Analyze = struct {
420458 return self.coerceArrayPtrToSlice(dest_type, inst);
421459 }
422460 }
461
462 // comptime_int to fixed-width integer
463 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
464 // The representation is already correct; we only need to make sure it fits in the destination type.
465 const val = inst.value().?; // comptime_int always has comptime known value
466 if (!val.intFitsInType(dest_type, self.target)) {
467 return self.fail(inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
468 }
469 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
470 }
471
423472 return self.fail(inst.src, "TODO implement type coercion", .{});
424473 }
425474
src-self-hosted/ir/text.zig+14
......@@ -28,6 +28,7 @@ pub const Inst = struct {
2828 @"export",
2929 primitive,
3030 fntype,
31 intcast,
3132 };
3233
3334 pub fn TagToType(tag: Tag) type {
......@@ -44,6 +45,7 @@ pub const Inst = struct {
4445 .@"export" => Export,
4546 .primitive => Primitive,
4647 .fntype => FnType,
48 .intcast => IntCast,
4749 };
4850 }
4951
......@@ -243,6 +245,17 @@ pub const Inst = struct {
243245 cc: std.builtin.CallingConvention = .Unspecified,
244246 },
245247 };
248
249 pub const IntCast = struct {
250 pub const base_tag = Tag.intcast;
251 base: Inst,
252
253 positionals: struct {
254 dest_type: *Inst,
255 value: *Inst,
256 },
257 kw_args: struct {},
258 };
246259};
247260
248261pub const ErrorMsg = struct {
......@@ -315,6 +328,7 @@ pub const Module = struct {
315328 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
316329 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
317330 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
331 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
318332 }
319333 }
320334
src-self-hosted/type.zig+162
......@@ -2,6 +2,7 @@ const std = @import("std");
22const Value = @import("value.zig").Value;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
5const Target = std.Target;
56
67/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
78/// It's important for this struct to be small.
......@@ -333,6 +334,44 @@ pub const Type = extern union {
333334 };
334335 }
335336
337 /// Asserts the type is a fixed-width integer.
338 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
339 return switch (self.tag()) {
340 .@"f16",
341 .@"f32",
342 .@"f64",
343 .@"f128",
344 .@"c_longdouble",
345 .@"c_void",
346 .@"bool",
347 .@"void",
348 .@"type",
349 .@"anyerror",
350 .@"comptime_int",
351 .@"comptime_float",
352 .@"noreturn",
353 .fn_naked_noreturn_no_args,
354 .array,
355 .single_const_pointer,
356 .array_u8_sentinel_0,
357 .const_slice_u8,
358 => unreachable,
359
360 .@"u8" => .{ .signed = false, .bits = 8 },
361 .@"i8" => .{ .signed = true, .bits = 8 },
362 .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
363 .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
364 .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) },
365 .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) },
366 .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) },
367 .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) },
368 .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) },
369 .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) },
370 .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) },
371 .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) },
372 };
373 }
374
336375 /// This enum does not directly correspond to `std.builtin.TypeId` because
337376 /// it has extra enum tags in it, as a way of using less memory. For example,
338377 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
......@@ -401,3 +440,126 @@ pub const Type = extern union {
401440 };
402441 };
403442};
443
444pub const CInteger = enum {
445 short,
446 ushort,
447 int,
448 uint,
449 long,
450 ulong,
451 longlong,
452 ulonglong,
453
454 pub fn sizeInBits(self: CInteger, target: Target) u16 {
455 const arch = target.cpu.arch;
456 switch (target.os.tag) {
457 .freestanding, .other => switch (target.cpu.arch) {
458 .msp430 => switch (self) {
459 .short,
460 .ushort,
461 .int,
462 .uint,
463 => return 16,
464 .long,
465 .ulong,
466 => return 32,
467 .longlong,
468 .ulonglong,
469 => return 64,
470 },
471 else => switch (self) {
472 .short,
473 .ushort,
474 => return 16,
475 .int,
476 .uint,
477 => return 32,
478 .long,
479 .ulong,
480 => return target.cpu.arch.ptrBitWidth(),
481 .longlong,
482 .ulonglong,
483 => return 64,
484 },
485 },
486
487 .linux,
488 .macosx,
489 .freebsd,
490 .netbsd,
491 .dragonfly,
492 .openbsd,
493 .wasi,
494 .emscripten,
495 => switch (self) {
496 .short,
497 .ushort,
498 => return 16,
499 .int,
500 .uint,
501 => return 32,
502 .long,
503 .ulong,
504 => return target.cpu.arch.ptrBitWidth(),
505 .longlong,
506 .ulonglong,
507 => return 64,
508 },
509
510 .windows, .uefi => switch (self) {
511 .short,
512 .ushort,
513 => return 16,
514 .int,
515 .uint,
516 .long,
517 .ulong,
518 => return 32,
519 .longlong,
520 .ulonglong,
521 => return 64,
522 },
523
524 .ios => switch (self) {
525 .short,
526 .ushort,
527 => return 16,
528 .int,
529 .uint,
530 => return 32,
531 .long,
532 .ulong,
533 .longlong,
534 .ulonglong,
535 => return 64,
536 },
537
538 .ananas,
539 .cloudabi,
540 .fuchsia,
541 .kfreebsd,
542 .lv2,
543 .solaris,
544 .haiku,
545 .minix,
546 .rtems,
547 .nacl,
548 .cnk,
549 .aix,
550 .cuda,
551 .nvcl,
552 .amdhsa,
553 .ps4,
554 .elfiamcu,
555 .tvos,
556 .watchos,
557 .mesa3d,
558 .contiki,
559 .amdpal,
560 .hermit,
561 .hurd,
562 => @panic("TODO specify the C integer type sizes for this OS"),
563 }
564 }
565};
src-self-hosted/value.zig+73
......@@ -3,6 +3,7 @@ const Type = @import("type.zig").Type;
33const log2 = std.math.log2;
44const assert = std.debug.assert;
55const BigInt = std.math.big.Int;
6const Target = std.Target;
67
78/// This is the raw data, with no bookkeeping, no memory awareness,
89/// no de-duplication, and no type system awareness.
......@@ -198,6 +199,78 @@ pub const Value = extern union {
198199 };
199200 }
200201
202 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
203 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
204 switch (self.tag()) {
205 .ty,
206 .u8_type,
207 .i8_type,
208 .isize_type,
209 .usize_type,
210 .c_short_type,
211 .c_ushort_type,
212 .c_int_type,
213 .c_uint_type,
214 .c_long_type,
215 .c_ulong_type,
216 .c_longlong_type,
217 .c_ulonglong_type,
218 .c_longdouble_type,
219 .f16_type,
220 .f32_type,
221 .f64_type,
222 .f128_type,
223 .c_void_type,
224 .bool_type,
225 .void_type,
226 .type_type,
227 .anyerror_type,
228 .comptime_int_type,
229 .comptime_float_type,
230 .noreturn_type,
231 .fn_naked_noreturn_no_args_type,
232 .const_slice_u8_type,
233 .void_value,
234 .noreturn_value,
235 .bool_true,
236 .bool_false,
237 .function,
238 .ref,
239 .bytes,
240 => unreachable,
241
242 .int_u64 => switch (ty.zigTypeTag()) {
243 .Int => {
244 const x = self.cast(Payload.Int_u64).?.int;
245 const info = ty.intInfo(target);
246 const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signed);
247 return info.bits >= needed_bits;
248 },
249 .ComptimeInt => return true,
250 else => unreachable,
251 },
252 .int_i64 => switch (ty.zigTypeTag()) {
253 .Int => {
254 const x = self.cast(Payload.Int_i64).?.int;
255 const info = ty.intInfo(target);
256 if (!info.signed and x < 0)
257 return false;
258 @panic("TODO implement i64 intFitsInType");
259 },
260 .ComptimeInt => return true,
261 else => unreachable,
262 },
263 .int_big => switch (ty.zigTypeTag()) {
264 .Int => {
265 const info = ty.intInfo(target);
266 return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);
267 },
268 .ComptimeInt => return true,
269 else => unreachable,
270 },
271 }
272 }
273
201274 /// This type is not copyable since it may contain pointers to its inner data.
202275 pub const Payload = struct {
203276 tag: Tag,
src/ir.cpp+3-3
......@@ -11289,9 +11289,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction
1128911289 Buf *val_buf = buf_alloc();
1129011290 bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
1129111291 ir_add_error_node(ira, instruction->base.source_node,
11292 buf_sprintf("integer value %s has no representation in type '%s'",
11293 buf_ptr(val_buf),
11294 buf_ptr(&other_type->name)));
11292 buf_sprintf("type %s cannot represent integer value %s",
11293 buf_ptr(&other_type->name),
11294 buf_ptr(val_buf)));
1129511295 return false;
1129611296 }
1129711297 if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {