authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 13:50:04-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 13:50:04-04:00
log9a2ea5ca420c7fdd4ab22b1f8814fa068d8ac56e
treee54e3998be254ae49bf62e08ecf13c051f557a03
parentfb63ba25779a4e482d2acc4c2cfc265c68c1b1eb

ir: comptime coerceArrayPtrToSlice


2 files changed, 66 insertions(+), 33 deletions(-)

src-self-hosted/ir.zig+63-31
...@@ -14,7 +14,8 @@ const text = @import("ir/text.zig");...@@ -14,7 +14,8 @@ const text = @import("ir/text.zig");
14pub const Inst = struct {14pub const Inst = struct {
15 tag: Tag,15 tag: Tag,
16 ty: Type,16 ty: Type,
17 src_offset: usize,17 /// Byte offset into the source.
18 src: usize,
1819
19 pub const Tag = enum {20 pub const Tag = enum {
20 unreach,21 unreach,
...@@ -29,6 +30,15 @@ pub const Inst = struct {...@@ -29,6 +30,15 @@ pub const Inst = struct {
29 return @fieldParentPtr(T, "base", base);30 return @fieldParentPtr(T, "base", base);
30 }31 }
3132
33 /// Returns `null` if runtime-known.
34 pub fn value(base: *Inst) ?Value {
35 return switch (base.tag) {
36 .unreach => Value.initTag(.noreturn_value),
37 .constant => base.cast(Constant).?.val,
38 .assembly => null,
39 };
40 }
41
32 pub const Constant = struct {42 pub const Constant = struct {
33 pub const base_tag = Tag.constant;43 pub const base_tag = Tag.constant;
34 base: Inst,44 base: Inst,
...@@ -156,9 +166,7 @@ const Analyze = struct {...@@ -156,9 +166,7 @@ const Analyze = struct {
156 }166 }
157167
158 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {168 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {
159 const const_inst = base.cast(Inst.Constant) orelse169 return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});
160 return self.fail(base.src_offset, "unable to resolve comptime value", .{});
161 return const_inst.val;
162 }170 }
163171
164 fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 {172 fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 {
...@@ -176,7 +184,7 @@ const Analyze = struct {...@@ -176,7 +184,7 @@ const Analyze = struct {
176 switch (typed_value.ty.zigTypeTag()) {184 switch (typed_value.ty.zigTypeTag()) {
177 .Fn => {},185 .Fn => {},
178 else => return self.fail(186 else => return self.fail(
179 export_inst.positionals.value.src_offset,187 export_inst.positionals.value.src,
180 "unable to export type '{}'",188 "unable to export type '{}'",
181 .{typed_value.ty},189 .{typed_value.ty},
182 ),190 ),
...@@ -187,7 +195,20 @@ const Analyze = struct {...@@ -187,7 +195,20 @@ const Analyze = struct {
187 });195 });
188 }196 }
189197
190 fn constStr(self: *Analyze, src_offset: usize, str: []const u8) !*Inst {198 fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst {
199 const const_inst = try self.arena.allocator.create(Inst.Constant);
200 const_inst.* = .{
201 .base = .{
202 .tag = Inst.Constant.base_tag,
203 .ty = typed_value.ty,
204 .src = src,
205 },
206 .val = typed_value.val,
207 };
208 return &const_inst.base;
209 }
210
211 fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst {
191 const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0);212 const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0);
192 array_payload.* = .{ .len = str.len };213 array_payload.* = .{ .len = str.len };
193214
...@@ -197,16 +218,10 @@ const Analyze = struct {...@@ -197,16 +218,10 @@ const Analyze = struct {
197 const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes);218 const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes);
198 bytes_payload.* = .{ .data = str };219 bytes_payload.* = .{ .data = str };
199220
200 const const_inst = try self.arena.allocator.create(Inst.Constant);221 return self.constInst(src, .{
201 const_inst.* = .{222 .ty = Type.initPayload(&ty_payload.base),
202 .base = .{
203 .tag = Inst.Constant.base_tag,
204 .ty = Type.initPayload(&ty_payload.base),
205 .src_offset = src_offset,
206 },
207 .val = Value.initPayload(&bytes_payload.base),223 .val = Value.initPayload(&bytes_payload.base),
208 };224 });
209 return &const_inst.base;
210 }225 }
211226
212 fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst {227 fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst {
...@@ -215,23 +230,28 @@ const Analyze = struct {...@@ -215,23 +230,28 @@ const Analyze = struct {
215 // We can use this reference because Inst.Const's Value is arena-allocated.230 // We can use this reference because Inst.Const's Value is arena-allocated.
216 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.231 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
217 const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes;232 const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes;
218 return self.constStr(old_inst.src_offset, bytes);233 return self.constStr(old_inst.src, bytes);
219 },234 },
220 .int => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),235 .int => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
221 .ptrtoint => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),236 .ptrtoint => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
222 .fieldptr => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),237 .fieldptr => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
223 .deref => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),238 .deref => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
224 .as => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),239 .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
225 .@"asm" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),240 .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
226 .@"unreachable" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),241 .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
227 .@"fn" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),242 .@"fn" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
228 .@"export" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),243 .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
229 .primitive => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),244 .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
230 .fntype => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),245 .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
231 }246 }
232 }247 }
233248
234 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {249 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
250 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
251 if (in_memory_result == .ok) {
252 return self.bitcast(dest_type, inst);
253 }
254
235 // *[N]T to []T255 // *[N]T to []T
236 if (inst.ty.isSinglePointer() and dest_type.isSlice() and256 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
237 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))257 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
...@@ -241,17 +261,29 @@ const Analyze = struct {...@@ -241,17 +261,29 @@ const Analyze = struct {
241 if (array_type.zigTypeTag() == .Array and261 if (array_type.zigTypeTag() == .Array and
242 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)262 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
243 {263 {
244 return self.fail(inst.src_offset, "TODO do the type coercion", .{});264 return self.coerceArrayPtrToSlice(dest_type, inst);
245 }265 }
246 }266 }
247 return self.fail(inst.src_offset, "TODO implement type coercion", .{});267 return self.fail(inst.src, "TODO implement type coercion", .{});
268 }
269
270 fn bitcast(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
271 return self.fail(inst.src, "TODO implement bitcast analysis", .{});
272 }
273
274 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
275 if (inst.value()) |val| {
276 // The comptime Value representation is compatible with both types.
277 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
278 }
279 return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
248 }280 }
249281
250 fn fail(self: *Analyze, src_offset: usize, comptime format: []const u8, args: var) InnerError {282 fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError {
251 @setCold(true);283 @setCold(true);
252 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);284 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
253 (try self.errors.addOne()).* = .{285 (try self.errors.addOne()).* = .{
254 .byte_offset = src_offset,286 .byte_offset = src,
255 .msg = msg,287 .msg = msg,
256 };288 };
257 return error.AnalysisFail;289 return error.AnalysisFail;
src-self-hosted/ir/text.zig+3-2
...@@ -12,7 +12,8 @@ const BigInt = std.math.big.Int;...@@ -12,7 +12,8 @@ const BigInt = std.math.big.Int;
12/// in-memory, analyzed instructions with types and values.12/// in-memory, analyzed instructions with types and values.
13pub const Inst = struct {13pub const Inst = struct {
14 tag: Tag,14 tag: Tag,
15 src_offset: usize,15 /// Byte offset into the source.
16 src: usize,
1617
17 /// These names are used directly as the instruction names in the text format.18 /// These names are used directly as the instruction names in the text format.
18 pub const Tag = enum {19 pub const Tag = enum {
...@@ -599,7 +600,7 @@ const Parser = struct {...@@ -599,7 +600,7 @@ const Parser = struct {
599 ) !*Inst {600 ) !*Inst {
600 const inst_specific = try self.arena.allocator.create(InstType);601 const inst_specific = try self.arena.allocator.create(InstType);
601 inst_specific.base = .{602 inst_specific.base = .{
602 .src_offset = self.i,603 .src = self.i,
603 .tag = InstType.base_tag,604 .tag = InstType.base_tag,
604 };605 };
605606