| ... | ... | @@ -14,7 +14,8 @@ const text = @import("ir/text.zig"); |
| 14 | 14 | pub const Inst = struct { |
| 15 | 15 | tag: Tag, |
| 16 | 16 | ty: Type, |
| 17 | | src_offset: usize, |
| 17 | /// Byte offset into the source. |
| 18 | src: usize, |
| 18 | 19 | |
| 19 | 20 | pub const Tag = enum { |
| 20 | 21 | unreach, |
| ... | ... | @@ -29,6 +30,15 @@ pub const Inst = struct { |
| 29 | 30 | return @fieldParentPtr(T, "base", base); |
| 30 | 31 | } |
| 31 | 32 | |
| 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 | 42 | pub const Constant = struct { |
| 33 | 43 | pub const base_tag = Tag.constant; |
| 34 | 44 | base: Inst, |
| ... | ... | @@ -156,9 +166,7 @@ const Analyze = struct { |
| 156 | 166 | } |
| 157 | 167 | |
| 158 | 168 | fn resolveConstValue(self: *Analyze, base: *Inst) !Value { |
| 159 | | const const_inst = base.cast(Inst.Constant) orelse |
| 160 | | return self.fail(base.src_offset, "unable to resolve comptime value", .{}); |
| 161 | | return const_inst.val; |
| 169 | return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{}); |
| 162 | 170 | } |
| 163 | 171 | |
| 164 | 172 | fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 { |
| ... | ... | @@ -176,7 +184,7 @@ const Analyze = struct { |
| 176 | 184 | switch (typed_value.ty.zigTypeTag()) { |
| 177 | 185 | .Fn => {}, |
| 178 | 186 | else => return self.fail( |
| 179 | | export_inst.positionals.value.src_offset, |
| 187 | export_inst.positionals.value.src, |
| 180 | 188 | "unable to export type '{}'", |
| 181 | 189 | .{typed_value.ty}, |
| 182 | 190 | ), |
| ... | ... | @@ -187,7 +195,20 @@ const Analyze = struct { |
| 187 | 195 | }); |
| 188 | 196 | } |
| 189 | 197 | |
| 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 | 212 | const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0); |
| 192 | 213 | array_payload.* = .{ .len = str.len }; |
| 193 | 214 | |
| ... | ... | @@ -197,16 +218,10 @@ const Analyze = struct { |
| 197 | 218 | const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes); |
| 198 | 219 | bytes_payload.* = .{ .data = str }; |
| 199 | 220 | |
| 200 | | const const_inst = try self.arena.allocator.create(Inst.Constant); |
| 201 | | const_inst.* = .{ |
| 202 | | .base = .{ |
| 203 | | .tag = Inst.Constant.base_tag, |
| 204 | | .ty = Type.initPayload(&ty_payload.base), |
| 205 | | .src_offset = src_offset, |
| 206 | | }, |
| 221 | return self.constInst(src, .{ |
| 222 | .ty = Type.initPayload(&ty_payload.base), |
| 207 | 223 | .val = Value.initPayload(&bytes_payload.base), |
| 208 | | }; |
| 209 | | return &const_inst.base; |
| 224 | }); |
| 210 | 225 | } |
| 211 | 226 | |
| 212 | 227 | fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst { |
| ... | ... | @@ -215,23 +230,28 @@ const Analyze = struct { |
| 215 | 230 | // We can use this reference because Inst.Const's Value is arena-allocated. |
| 216 | 231 | // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends. |
| 217 | 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)}), |
| 221 | | .ptrtoint => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 222 | | .fieldptr => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 223 | | .deref => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 224 | | .as => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 225 | | .@"asm" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 226 | | .@"unreachable" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 227 | | .@"fn" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 228 | | .@"export" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 229 | | .primitive => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 230 | | .fntype => 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)}), |
| 236 | .ptrtoint => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 237 | .fieldptr => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 238 | .deref => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 239 | .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 240 | .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 241 | .@"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)}), |
| 243 | .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 244 | .primitive => return self.fail(old_inst.src, "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 | } |
| 233 | 248 | |
| 234 | 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 | 255 | // *[N]T to []T |
| 236 | 256 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and |
| 237 | 257 | (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) |
| ... | ... | @@ -241,17 +261,29 @@ const Analyze = struct { |
| 241 | 261 | if (array_type.zigTypeTag() == .Array and |
| 242 | 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 | } |
| 249 | 281 | |
| 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 | 283 | @setCold(true); |
| 252 | 284 | const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args); |
| 253 | 285 | (try self.errors.addOne()).* = .{ |
| 254 | | .byte_offset = src_offset, |
| 286 | .byte_offset = src, |
| 255 | 287 | .msg = msg, |
| 256 | 288 | }; |
| 257 | 289 | return error.AnalysisFail; |