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 {...@@ -237,7 +237,7 @@ pub const Int = struct {
237 return bits;237 return bits;
238 }238 }
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 {
241 if (self.eqZero()) {241 if (self.eqZero()) {
242 return true;242 return true;
243 }243 }
lib/std/target.zig+1-1
...@@ -761,7 +761,7 @@ pub const Target = struct {...@@ -761,7 +761,7 @@ pub const Target = struct {
761 };761 };
762 }762 }
763763
764 pub fn ptrBitWidth(arch: Arch) u32 {764 pub fn ptrBitWidth(arch: Arch) u16 {
765 switch (arch) {765 switch (arch) {
766 .avr,766 .avr,
767 .msp430,767 .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;...@@ -6,6 +6,7 @@ const Type = @import("type.zig").Type;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const text = @import("ir/text.zig");7const text = @import("ir/text.zig");
8const BigInt = std.math.big.Int;8const BigInt = std.math.big.Int;
9const Target = std.Target;
910
10/// These are in-memory, analyzed instructions. See `text.Inst` for the representation11/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
11/// of instructions that correspond to the ZIR text format.12/// of instructions that correspond to the ZIR text format.
...@@ -99,6 +100,8 @@ pub const ErrorMsg = struct {...@@ -99,6 +100,8 @@ pub const ErrorMsg = struct {
99};100};
100101
101pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {102pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
103 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
104
102 var ctx = Analyze{105 var ctx = Analyze{
103 .allocator = allocator,106 .allocator = allocator,
104 .arena = std.heap.ArenaAllocator.init(allocator),107 .arena = std.heap.ArenaAllocator.init(allocator),
...@@ -107,6 +110,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {...@@ -107,6 +110,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
107 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),110 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
108 .exports = std.ArrayList(Module.Export).init(allocator),111 .exports = std.ArrayList(Module.Export).init(allocator),
109 .fns = std.ArrayList(Module.Fn).init(allocator),112 .fns = std.ArrayList(Module.Fn).init(allocator),
113 .target = native_info.target,
110 };114 };
111 defer ctx.errors.deinit();115 defer ctx.errors.deinit();
112 defer ctx.decl_table.deinit();116 defer ctx.decl_table.deinit();
...@@ -135,6 +139,7 @@ const Analyze = struct {...@@ -135,6 +139,7 @@ const Analyze = struct {
135 decl_table: std.AutoHashMap(*text.Inst, NewDecl),139 decl_table: std.AutoHashMap(*text.Inst, NewDecl),
136 exports: std.ArrayList(Module.Export),140 exports: std.ArrayList(Module.Export),
137 fns: std.ArrayList(Module.Fn),141 fns: std.ArrayList(Module.Fn),
142 target: Target,
138143
139 const NewDecl = struct {144 const NewDecl = struct {
140 /// null means a semantic analysis error happened145 /// null means a semantic analysis error happened
...@@ -336,6 +341,7 @@ const Analyze = struct {...@@ -336,6 +341,7 @@ const Analyze = struct {
336 .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),341 .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
337 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),342 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
338 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),343 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
344 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
339 }345 }
340 }346 }
341347
...@@ -402,6 +408,38 @@ const Analyze = struct {...@@ -402,6 +408,38 @@ const Analyze = struct {
402 return self.coerce(dest_type, new_inst);408 return self.coerce(dest_type, new_inst);
403 }409 }
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
405 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {443 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
406 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);444 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
407 if (in_memory_result == .ok) {445 if (in_memory_result == .ok) {
...@@ -420,6 +458,17 @@ const Analyze = struct {...@@ -420,6 +458,17 @@ const Analyze = struct {
420 return self.coerceArrayPtrToSlice(dest_type, inst);458 return self.coerceArrayPtrToSlice(dest_type, inst);
421 }459 }
422 }460 }
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
423 return self.fail(inst.src, "TODO implement type coercion", .{});472 return self.fail(inst.src, "TODO implement type coercion", .{});
424 }473 }
425474
src-self-hosted/ir/text.zig+14
...@@ -28,6 +28,7 @@ pub const Inst = struct {...@@ -28,6 +28,7 @@ pub const Inst = struct {
28 @"export",28 @"export",
29 primitive,29 primitive,
30 fntype,30 fntype,
31 intcast,
31 };32 };
3233
33 pub fn TagToType(tag: Tag) type {34 pub fn TagToType(tag: Tag) type {
...@@ -44,6 +45,7 @@ pub const Inst = struct {...@@ -44,6 +45,7 @@ pub const Inst = struct {
44 .@"export" => Export,45 .@"export" => Export,
45 .primitive => Primitive,46 .primitive => Primitive,
46 .fntype => FnType,47 .fntype => FnType,
48 .intcast => IntCast,
47 };49 };
48 }50 }
4951
...@@ -243,6 +245,17 @@ pub const Inst = struct {...@@ -243,6 +245,17 @@ pub const Inst = struct {
243 cc: std.builtin.CallingConvention = .Unspecified,245 cc: std.builtin.CallingConvention = .Unspecified,
244 },246 },
245 };247 };
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 };
246};259};
247260
248pub const ErrorMsg = struct {261pub const ErrorMsg = struct {
...@@ -315,6 +328,7 @@ pub const Module = struct {...@@ -315,6 +328,7 @@ pub const Module = struct {
315 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),328 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
316 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),329 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
317 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),330 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
331 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
318 }332 }
319 }333 }
320334
src-self-hosted/type.zig+162
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const Value = @import("value.zig").Value;2const Value = @import("value.zig").Value;
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const Target = std.Target;
56
6/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.7/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
7/// It's important for this struct to be small.8/// It's important for this struct to be small.
...@@ -333,6 +334,44 @@ pub const Type = extern union {...@@ -333,6 +334,44 @@ pub const Type = extern union {
333 };334 };
334 }335 }
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
336 /// This enum does not directly correspond to `std.builtin.TypeId` because375 /// This enum does not directly correspond to `std.builtin.TypeId` because
337 /// it has extra enum tags in it, as a way of using less memory. For example,376 /// it has extra enum tags in it, as a way of using less memory. For example,
338 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types377 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
...@@ -401,3 +440,126 @@ pub const Type = extern union {...@@ -401,3 +440,126 @@ pub const Type = extern union {
401 };440 };
402 };441 };
403};442};
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;...@@ -3,6 +3,7 @@ const Type = @import("type.zig").Type;
3const log2 = std.math.log2;3const log2 = std.math.log2;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const BigInt = std.math.big.Int;5const BigInt = std.math.big.Int;
6const Target = std.Target;
67
7/// This is the raw data, with no bookkeeping, no memory awareness,8/// This is the raw data, with no bookkeeping, no memory awareness,
8/// no de-duplication, and no type system awareness.9/// no de-duplication, and no type system awareness.
...@@ -198,6 +199,78 @@ pub const Value = extern union {...@@ -198,6 +199,78 @@ pub const Value = extern union {
198 };199 };
199 }200 }
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
201 /// This type is not copyable since it may contain pointers to its inner data.274 /// This type is not copyable since it may contain pointers to its inner data.
202 pub const Payload = struct {275 pub const Payload = struct {
203 tag: Tag,276 tag: Tag,
src/ir.cpp+3-3
...@@ -11289,9 +11289,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction...@@ -11289,9 +11289,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction
11289 Buf *val_buf = buf_alloc();11289 Buf *val_buf = buf_alloc();
11290 bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);11290 bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
11291 ir_add_error_node(ira, instruction->base.source_node,11291 ir_add_error_node(ira, instruction->base.source_node,
11292 buf_sprintf("integer value %s has no representation in type '%s'",11292 buf_sprintf("type %s cannot represent integer value %s",
11293 buf_ptr(val_buf),11293 buf_ptr(&other_type->name),
11294 buf_ptr(&other_type->name)));11294 buf_ptr(val_buf)));
11295 return false;11295 return false;
11296 }11296 }
11297 if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {11297 if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {