authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 16:06:15-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 16:06:15-04:00
log22e7ca5613c32b14043dee4531fe8b180bfc407a
tree9c802426d9ee56ac6554b0aaf010e956bd492507
parent9a2ea5ca420c7fdd4ab22b1f8814fa068d8ac56e

ir: analysis of fn instruction


3 files changed, 115 insertions(+), 20 deletions(-)

lib/std/mem.zig+1-1
......@@ -118,7 +118,7 @@ pub const Allocator = struct {
118118 ptr[n] = sentinel;
119119 return ptr[0..n :sentinel];
120120 } else {
121 return alignedAlloc(Elem, optional_alignment, n);
121 return self.alignedAlloc(Elem, optional_alignment, n);
122122 }
123123 }
124124
src-self-hosted/ir.zig+89-19
......@@ -68,12 +68,18 @@ pub const Module = struct {
6868 exports: []Export,
6969 errors: []ErrorMsg,
7070 arena: std.heap.ArenaAllocator,
71 fns: []Fn,
7172
7273 pub const Export = struct {
7374 name: []const u8,
7475 typed_value: TypedValue,
7576 };
7677
78 pub const Fn = struct {
79 analysis_status: enum { in_progress, failure, success },
80 body: []*Inst,
81 };
82
7783 pub fn deinit(self: *Module, allocator: *Allocator) void {
7884 allocator.free(self.exports);
7985 allocator.free(self.errors);
......@@ -97,12 +103,14 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
97103 .arena = std.heap.ArenaAllocator.init(allocator),
98104 .old_module = &old_module,
99105 .errors = std.ArrayList(ErrorMsg).init(allocator),
100 .inst_table = std.AutoHashMap(*text.Inst, Analyze.NewInst).init(allocator),
106 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
101107 .exports = std.ArrayList(Module.Export).init(allocator),
108 .fns = std.ArrayList(Module.Fn).init(allocator),
102109 };
103110 defer ctx.errors.deinit();
104 defer ctx.inst_table.deinit();
111 defer ctx.decl_table.deinit();
105112 defer ctx.exports.deinit();
113 defer ctx.fns.deinit();
106114
107115 ctx.analyzeRoot() catch |err| switch (err) {
108116 error.AnalysisFail => {
......@@ -113,6 +121,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
113121 return Module{
114122 .exports = ctx.exports.toOwnedSlice(),
115123 .errors = ctx.errors.toOwnedSlice(),
124 .fns = ctx.fns.toOwnedSlice(),
116125 .arena = ctx.arena,
117126 };
118127}
......@@ -122,42 +131,57 @@ const Analyze = struct {
122131 arena: std.heap.ArenaAllocator,
123132 old_module: *const text.Module,
124133 errors: std.ArrayList(ErrorMsg),
125 inst_table: std.AutoHashMap(*text.Inst, NewInst),
134 decl_table: std.AutoHashMap(*text.Inst, NewDecl),
126135 exports: std.ArrayList(Module.Export),
136 fns: std.ArrayList(Module.Fn),
127137
128 const NewInst = struct {
138 const NewDecl = struct {
129139 /// null means a semantic analysis error happened
130140 ptr: ?*Inst,
131141 };
132142
143 const NewInst = struct {
144 ptr: *Inst,
145 };
146
147 const Fn = struct {
148 body: std.ArrayList(*Inst),
149 inst_table: std.AutoHashMap(*text.Inst, NewInst),
150 /// Index into Module fns array
151 fn_index: usize,
152 };
153
133154 const InnerError = error{ OutOfMemory, AnalysisFail };
134155
135156 fn analyzeRoot(self: *Analyze) !void {
136157 for (self.old_module.decls) |decl| {
137158 if (decl.cast(text.Inst.Export)) |export_inst| {
138 try analyzeExport(self, export_inst);
159 try analyzeExport(self, null, export_inst);
139160 }
140161 }
141162 }
142163
143 fn resolveInst(self: *Analyze, old_inst: *text.Inst) InnerError!*Inst {
144 if (self.inst_table.get(old_inst)) |kv| {
164 fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
165 if (opt_func) |func| {
166 const kv = func.inst_table.get(old_inst) orelse return error.AnalysisFail;
167 return kv.value.ptr;
168 } else if (self.decl_table.get(old_inst)) |kv| {
145169 return kv.value.ptr orelse return error.AnalysisFail;
146170 } else {
147 const new_inst = self.analyzeDecl(old_inst) catch |err| switch (err) {
171 const new_inst = self.analyzeInst(old_inst, null) catch |err| switch (err) {
148172 error.AnalysisFail => {
149 try self.inst_table.putNoClobber(old_inst, .{ .ptr = null });
173 try self.decl_table.putNoClobber(old_inst, .{ .ptr = null });
150174 return error.AnalysisFail;
151175 },
152176 else => |e| return e,
153177 };
154 try self.inst_table.putNoClobber(old_inst, .{ .ptr = new_inst });
178 try self.decl_table.putNoClobber(old_inst, .{ .ptr = new_inst });
155179 return new_inst;
156180 }
157181 }
158182
159 fn resolveInstConst(self: *Analyze, old_inst: *text.Inst) InnerError!TypedValue {
160 const new_inst = try self.resolveInst(old_inst);
183 fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue {
184 const new_inst = try self.resolveInst(func, old_inst);
161185 const val = try self.resolveConstValue(new_inst);
162186 return TypedValue{
163187 .ty = new_inst.ty,
......@@ -169,17 +193,25 @@ const Analyze = struct {
169193 return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});
170194 }
171195
172 fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 {
173 const new_inst = try self.resolveInst(old_inst);
196 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
197 const new_inst = try self.resolveInst(func, old_inst);
174198 const wanted_type = Type.initTag(.const_slice_u8);
175199 const coerced_inst = try self.coerce(wanted_type, new_inst);
176200 const val = try self.resolveConstValue(coerced_inst);
177201 return val.toAllocatedBytes(&self.arena.allocator);
178202 }
179203
180 fn analyzeExport(self: *Analyze, export_inst: *text.Inst.Export) !void {
181 const symbol_name = try self.resolveConstString(export_inst.positionals.symbol_name);
182 const typed_value = try self.resolveInstConst(export_inst.positionals.value);
204 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
205 const new_inst = try self.resolveInst(func, old_inst);
206 const wanted_type = Type.initTag(.@"type");
207 const coerced_inst = try self.coerce(wanted_type, new_inst);
208 const val = try self.resolveConstValue(coerced_inst);
209 return val.toType();
210 }
211
212 fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void {
213 const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name);
214 const typed_value = try self.resolveInstConst(func, export_inst.positionals.value);
183215
184216 switch (typed_value.ty.zigTypeTag()) {
185217 .Fn => {},
......@@ -224,7 +256,7 @@ const Analyze = struct {
224256 });
225257 }
226258
227 fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst {
259 fn analyzeInst(self: *Analyze, old_inst: *text.Inst, opt_func: ?*Fn) InnerError!*Inst {
228260 switch (old_inst.tag) {
229261 .str => {
230262 // We can use this reference because Inst.Const's Value is arena-allocated.
......@@ -239,7 +271,45 @@ const Analyze = struct {
239271 .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
240272 .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
241273 .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
242 .@"fn" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
274 .@"fn" => {
275 const fn_inst = old_inst.cast(text.Inst.Fn).?;
276 const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
277
278 var new_func: Fn = .{
279 .body = std.ArrayList(*Inst).init(self.allocator),
280 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
281 .fn_index = self.fns.items.len,
282 };
283 defer new_func.body.deinit();
284 defer new_func.inst_table.deinit();
285 // Don't hang on to a reference to this when analyzing body instructions, since the memory
286 // could become invalid.
287 (try self.fns.addOne()).* = .{
288 .analysis_status = .in_progress,
289 .body = undefined,
290 };
291
292 for (fn_inst.positionals.body.instructions) |src_inst| {
293 const new_inst = self.analyzeInst(src_inst, &new_func) catch |err| {
294 self.fns.items[new_func.fn_index].analysis_status = .failure;
295 return err;
296 };
297 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
298 }
299
300 self.fns.items[new_func.fn_index] = .{
301 .analysis_status = .success,
302 .body = new_func.body.toOwnedSlice(),
303 };
304
305 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
306 fn_payload.* = .{ .index = new_func.fn_index };
307
308 return self.constInst(old_inst.src, .{
309 .ty = fn_type,
310 .val = Value.initPayload(&fn_payload.base),
311 });
312 },
243313 .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
244314 .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
245315 .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
src-self-hosted/value.zig+25
......@@ -100,6 +100,29 @@ pub const Value = extern union {
100100 unreachable;
101101 }
102102
103 /// Asserts that the value is representable as a type.
104 pub fn toType(self: Value) Type {
105 return switch (self.tag()) {
106 .ty => self.cast(Payload.Ty).?.ty,
107
108 .void_type => Type.initTag(.@"void"),
109 .noreturn_type => Type.initTag(.@"noreturn"),
110 .bool_type => Type.initTag(.@"bool"),
111 .usize_type => Type.initTag(.@"usize"),
112
113 .void_value,
114 .noreturn_value,
115 .bool_true,
116 .bool_false,
117 .int_u64,
118 .int_i64,
119 .function,
120 .ref,
121 .bytes,
122 => unreachable,
123 };
124 }
125
103126 /// This type is not copyable since it may contain pointers to its inner data.
104127 pub const Payload = struct {
105128 tag: Tag,
......@@ -116,6 +139,8 @@ pub const Value = extern union {
116139
117140 pub const Function = struct {
118141 base: Payload = Payload{ .tag = .function },
142 /// Index into the `fns` array of the `ir.Module`
143 index: usize,
119144 };
120145
121146 pub const ArraySentinel0_u8_Type = struct {