| ... | ... | @@ -16,7 +16,6 @@ const Value = @import("../value.zig").Value; |
| 16 | 16 | const Type = @import("../type.zig").Type; |
| 17 | 17 | |
| 18 | 18 | const LazySrcLoc = Module.LazySrcLoc; |
| 19 | | const SrcLoc = Module.SrcLoc; |
| 20 | 19 | |
| 21 | 20 | pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 22 | 21 | const llvm_arch = switch (target.cpu.arch) { |
| ... | ... | @@ -146,83 +145,42 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 146 | 145 | return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); |
| 147 | 146 | } |
| 148 | 147 | |
| 149 | | pub const LLVMIRModule = struct { |
| 150 | | module: *Module, |
| 148 | pub const Object = struct { |
| 151 | 149 | llvm_module: *const llvm.Module, |
| 152 | 150 | context: *const llvm.Context, |
| 153 | 151 | target_machine: *const llvm.TargetMachine, |
| 154 | | builder: *const llvm.Builder, |
| 155 | | |
| 156 | | object_path: []const u8, |
| 157 | | |
| 158 | | gpa: *Allocator, |
| 159 | | err_msg: ?*Module.ErrorMsg = null, |
| 160 | | |
| 161 | | // TODO: The fields below should really move into a different struct, |
| 162 | | // because they are only valid when generating a function |
| 163 | | |
| 164 | | /// TODO: this should not be undefined since it should be in another per-decl struct |
| 165 | | /// Curent decl we are analysing. Stored to get source locations from relative info |
| 166 | | decl: *Module.Decl = undefined, |
| 167 | | |
| 168 | | /// This stores the LLVM values used in a function, such that they can be |
| 169 | | /// referred to in other instructions. This table is cleared before every function is generated. |
| 170 | | /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks |
| 171 | | /// in here, however if a block ends, the instructions can be thrown away. |
| 172 | | func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{}, |
| 173 | | |
| 174 | | /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. |
| 175 | | args: []*const llvm.Value = &[_]*const llvm.Value{}, |
| 176 | | arg_index: usize = 0, |
| 177 | | |
| 178 | | entry_block: *const llvm.BasicBlock = undefined, |
| 179 | | /// This fields stores the last alloca instruction, such that we can append more alloca instructions |
| 180 | | /// to the top of the function. |
| 181 | | latest_alloca_inst: ?*const llvm.Value = null, |
| 182 | | |
| 183 | | llvm_func: *const llvm.Value = undefined, |
| 184 | | |
| 185 | | /// This data structure is used to implement breaking to blocks. |
| 186 | | blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct { |
| 187 | | parent_bb: *const llvm.BasicBlock, |
| 188 | | break_bbs: *BreakBasicBlocks, |
| 189 | | break_vals: *BreakValues, |
| 190 | | }) = .{}, |
| 191 | | |
| 192 | | src_loc: Module.SrcLoc, |
| 193 | | |
| 194 | | const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); |
| 195 | | const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); |
| 152 | object_pathZ: [:0]const u8, |
| 196 | 153 | |
| 197 | | pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { |
| 198 | | const self = try allocator.create(LLVMIRModule); |
| 154 | pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object { |
| 155 | const self = try allocator.create(Object); |
| 199 | 156 | errdefer allocator.destroy(self); |
| 200 | 157 | |
| 201 | | const gpa = options.module.?.gpa; |
| 202 | | |
| 203 | | const obj_basename = try std.zig.binNameAlloc(gpa, .{ |
| 158 | const obj_basename = try std.zig.binNameAlloc(allocator, .{ |
| 204 | 159 | .root_name = options.root_name, |
| 205 | 160 | .target = options.target, |
| 206 | 161 | .output_mode = .Obj, |
| 207 | 162 | }); |
| 208 | | defer gpa.free(obj_basename); |
| 163 | defer allocator.free(obj_basename); |
| 209 | 164 | |
| 210 | 165 | const o_directory = options.module.?.zig_cache_artifact_directory; |
| 211 | | const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename}); |
| 212 | | errdefer gpa.free(object_path); |
| 166 | const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename}); |
| 167 | defer allocator.free(object_path); |
| 168 | |
| 169 | const object_pathZ = try allocator.dupeZ(u8, object_path); |
| 170 | errdefer allocator.free(object_pathZ); |
| 213 | 171 | |
| 214 | 172 | const context = llvm.Context.create(); |
| 215 | 173 | errdefer context.dispose(); |
| 216 | 174 | |
| 217 | 175 | initializeLLVMTargets(); |
| 218 | 176 | |
| 219 | | const root_nameZ = try gpa.dupeZ(u8, options.root_name); |
| 220 | | defer gpa.free(root_nameZ); |
| 177 | const root_nameZ = try allocator.dupeZ(u8, options.root_name); |
| 178 | defer allocator.free(root_nameZ); |
| 221 | 179 | const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); |
| 222 | 180 | errdefer llvm_module.dispose(); |
| 223 | 181 | |
| 224 | | const llvm_target_triple = try targetTriple(gpa, options.target); |
| 225 | | defer gpa.free(llvm_target_triple); |
| 182 | const llvm_target_triple = try targetTriple(allocator, options.target); |
| 183 | defer allocator.free(llvm_target_triple); |
| 226 | 184 | |
| 227 | 185 | var error_message: [*:0]const u8 = undefined; |
| 228 | 186 | var target: *const llvm.Target = undefined; |
| ... | ... | @@ -257,34 +215,21 @@ pub const LLVMIRModule = struct { |
| 257 | 215 | ); |
| 258 | 216 | errdefer target_machine.dispose(); |
| 259 | 217 | |
| 260 | | const builder = context.createBuilder(); |
| 261 | | errdefer builder.dispose(); |
| 262 | | |
| 263 | 218 | self.* = .{ |
| 264 | | .module = options.module.?, |
| 265 | 219 | .llvm_module = llvm_module, |
| 266 | 220 | .context = context, |
| 267 | 221 | .target_machine = target_machine, |
| 268 | | .builder = builder, |
| 269 | | .object_path = object_path, |
| 270 | | .gpa = gpa, |
| 271 | | // TODO move this field into a struct that is only instantiated per gen() call |
| 272 | | .src_loc = undefined, |
| 222 | .object_pathZ = object_pathZ, |
| 273 | 223 | }; |
| 274 | 224 | return self; |
| 275 | 225 | } |
| 276 | 226 | |
| 277 | | pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void { |
| 278 | | self.builder.dispose(); |
| 227 | pub fn deinit(self: *Object, allocator: *Allocator) void { |
| 279 | 228 | self.target_machine.dispose(); |
| 280 | 229 | self.llvm_module.dispose(); |
| 281 | 230 | self.context.dispose(); |
| 282 | 231 | |
| 283 | | self.func_inst_table.deinit(self.gpa); |
| 284 | | self.gpa.free(self.object_path); |
| 285 | | |
| 286 | | self.blocks.deinit(self.gpa); |
| 287 | | |
| 232 | allocator.free(self.object_pathZ); |
| 288 | 233 | allocator.destroy(self); |
| 289 | 234 | } |
| 290 | 235 | |
| ... | ... | @@ -296,7 +241,7 @@ pub const LLVMIRModule = struct { |
| 296 | 241 | llvm.initializeAllAsmParsers(); |
| 297 | 242 | } |
| 298 | 243 | |
| 299 | | pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void { |
| 244 | pub fn flushModule(self: *Object, comp: *Compilation) !void { |
| 300 | 245 | if (comp.verbose_llvm_ir) { |
| 301 | 246 | const dump = self.llvm_module.printToString(); |
| 302 | 247 | defer llvm.disposeMessage(dump); |
| ... | ... | @@ -317,13 +262,10 @@ pub const LLVMIRModule = struct { |
| 317 | 262 | } |
| 318 | 263 | } |
| 319 | 264 | |
| 320 | | const object_pathZ = try self.gpa.dupeZ(u8, self.object_path); |
| 321 | | defer self.gpa.free(object_pathZ); |
| 322 | | |
| 323 | 265 | var error_message: [*:0]const u8 = undefined; |
| 324 | 266 | if (self.target_machine.emitToFile( |
| 325 | 267 | self.llvm_module, |
| 326 | | object_pathZ.ptr, |
| 268 | self.object_pathZ.ptr, |
| 327 | 269 | .ObjectFile, |
| 328 | 270 | &error_message, |
| 329 | 271 | ).toBool()) { |
| ... | ... | @@ -335,23 +277,55 @@ pub const LLVMIRModule = struct { |
| 335 | 277 | } |
| 336 | 278 | } |
| 337 | 279 | |
| 338 | | pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { |
| 339 | | self.gen(module, decl) catch |err| switch (err) { |
| 280 | pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void { |
| 281 | var dg: DeclGen = .{ |
| 282 | .object = self, |
| 283 | .module = module, |
| 284 | .decl = decl, |
| 285 | .err_msg = null, |
| 286 | .gpa = module.gpa, |
| 287 | }; |
| 288 | dg.genDecl() catch |err| switch (err) { |
| 340 | 289 | error.CodegenFail => { |
| 341 | 290 | decl.analysis = .codegen_failure; |
| 342 | | try module.failed_decls.put(module.gpa, decl, self.err_msg.?); |
| 343 | | self.err_msg = null; |
| 291 | try module.failed_decls.put(module.gpa, decl, dg.err_msg.?); |
| 292 | dg.err_msg = null; |
| 344 | 293 | return; |
| 345 | 294 | }, |
| 346 | 295 | else => |e| return e, |
| 347 | 296 | }; |
| 348 | 297 | } |
| 298 | }; |
| 299 | |
| 300 | pub const DeclGen = struct { |
| 301 | object: *Object, |
| 302 | module: *Module, |
| 303 | decl: *Module.Decl, |
| 304 | err_msg: ?*Module.ErrorMsg, |
| 305 | |
| 306 | gpa: *Allocator, |
| 307 | |
| 308 | fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 309 | @setCold(true); |
| 310 | assert(self.err_msg == null); |
| 311 | const src_loc = src.toSrcLocWithDecl(self.decl); |
| 312 | self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, format, args); |
| 313 | return error.CodegenFail; |
| 314 | } |
| 315 | |
| 316 | fn llvmModule(self: *DeclGen) *const llvm.Module { |
| 317 | return self.object.llvm_module; |
| 318 | } |
| 319 | |
| 320 | fn context(self: *DeclGen) *const llvm.Context { |
| 321 | return self.object.context; |
| 322 | } |
| 349 | 323 | |
| 350 | | fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { |
| 324 | fn genDecl(self: *DeclGen) !void { |
| 325 | const decl = self.decl; |
| 351 | 326 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 352 | | self.src_loc = decl.srcLoc(); |
| 353 | | self.decl = decl; |
| 354 | | const src = self.src_loc.lazy; |
| 327 | |
| 328 | const src = decl.srcLoc().lazy; |
| 355 | 329 | |
| 356 | 330 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); |
| 357 | 331 | |
| ... | ... | @@ -363,16 +337,10 @@ pub const LLVMIRModule = struct { |
| 363 | 337 | // This gets the LLVM values from the function and stores them in `self.args`. |
| 364 | 338 | const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); |
| 365 | 339 | var args = try self.gpa.alloc(*const llvm.Value, fn_param_len); |
| 366 | | defer self.gpa.free(args); |
| 367 | 340 | |
| 368 | 341 | for (args) |*arg, i| { |
| 369 | 342 | arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); |
| 370 | 343 | } |
| 371 | | self.args = args; |
| 372 | | self.arg_index = 0; |
| 373 | | |
| 374 | | // Make sure no other LLVM values from other functions can be referenced |
| 375 | | self.func_inst_table.clearRetainingCapacity(); |
| 376 | 344 | |
| 377 | 345 | // We remove all the basic blocks of a function to support incremental |
| 378 | 346 | // compilation! |
| ... | ... | @@ -381,12 +349,25 @@ pub const LLVMIRModule = struct { |
| 381 | 349 | bb.deleteBasicBlock(); |
| 382 | 350 | } |
| 383 | 351 | |
| 384 | | self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry"); |
| 385 | | self.builder.positionBuilderAtEnd(self.entry_block); |
| 386 | | self.latest_alloca_inst = null; |
| 387 | | self.llvm_func = llvm_func; |
| 352 | const builder = self.context().createBuilder(); |
| 353 | |
| 354 | const entry_block = self.context().appendBasicBlock(llvm_func, "Entry"); |
| 355 | builder.positionBuilderAtEnd(entry_block); |
| 356 | |
| 357 | var fg: FuncGen = .{ |
| 358 | .dg = self, |
| 359 | .builder = builder, |
| 360 | .args = args, |
| 361 | .arg_index = 0, |
| 362 | .func_inst_table = .{}, |
| 363 | .entry_block = entry_block, |
| 364 | .latest_alloca_inst = null, |
| 365 | .llvm_func = llvm_func, |
| 366 | .blocks = .{}, |
| 367 | }; |
| 368 | defer fg.deinit(); |
| 388 | 369 | |
| 389 | | try self.genBody(func.body); |
| 370 | try fg.genBody(func.body); |
| 390 | 371 | } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { |
| 391 | 372 | _ = try self.resolveLLVMFunction(extern_fn.data, src); |
| 392 | 373 | } else { |
| ... | ... | @@ -394,7 +375,267 @@ pub const LLVMIRModule = struct { |
| 394 | 375 | } |
| 395 | 376 | } |
| 396 | 377 | |
| 397 | | fn genBody(self: *LLVMIRModule, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { |
| 378 | /// If the llvm function does not exist, create it |
| 379 | fn resolveLLVMFunction(self: *DeclGen, func: *Module.Decl, src: LazySrcLoc) !*const llvm.Value { |
| 380 | // TODO: do we want to store this in our own datastructure? |
| 381 | if (self.llvmModule().getNamedFunction(func.name)) |llvm_fn| return llvm_fn; |
| 382 | |
| 383 | const zig_fn_type = func.typed_value.most_recent.typed_value.ty; |
| 384 | const return_type = zig_fn_type.fnReturnType(); |
| 385 | |
| 386 | const fn_param_len = zig_fn_type.fnParamLen(); |
| 387 | |
| 388 | const fn_param_types = try self.gpa.alloc(Type, fn_param_len); |
| 389 | defer self.gpa.free(fn_param_types); |
| 390 | zig_fn_type.fnParamTypes(fn_param_types); |
| 391 | |
| 392 | const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len); |
| 393 | defer self.gpa.free(llvm_param); |
| 394 | |
| 395 | for (fn_param_types) |fn_param, i| { |
| 396 | llvm_param[i] = try self.getLLVMType(fn_param, src); |
| 397 | } |
| 398 | |
| 399 | const fn_type = llvm.Type.functionType( |
| 400 | try self.getLLVMType(return_type, src), |
| 401 | if (fn_param_len == 0) null else llvm_param.ptr, |
| 402 | @intCast(c_uint, fn_param_len), |
| 403 | .False, |
| 404 | ); |
| 405 | const llvm_fn = self.llvmModule().addFunction(func.name, fn_type); |
| 406 | |
| 407 | if (return_type.tag() == .noreturn) { |
| 408 | self.addFnAttr(llvm_fn, "noreturn"); |
| 409 | } |
| 410 | |
| 411 | return llvm_fn; |
| 412 | } |
| 413 | |
| 414 | fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl, src: LazySrcLoc) error{ OutOfMemory, CodegenFail }!*const llvm.Value { |
| 415 | // TODO: do we want to store this in our own datastructure? |
| 416 | if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val; |
| 417 | |
| 418 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 419 | |
| 420 | // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`. |
| 421 | const llvm_type = try self.getLLVMType(typed_value.ty, src); |
| 422 | const val = try self.genTypedValue(src, typed_value, null); |
| 423 | const global = self.llvmModule().addGlobal(llvm_type, decl.name); |
| 424 | llvm.setInitializer(global, val); |
| 425 | |
| 426 | // TODO ask the Decl if it is const |
| 427 | // https://github.com/ziglang/zig/issues/7582 |
| 428 | |
| 429 | return global; |
| 430 | } |
| 431 | |
| 432 | fn getLLVMType(self: *DeclGen, t: Type, src: LazySrcLoc) error{ OutOfMemory, CodegenFail }!*const llvm.Type { |
| 433 | switch (t.zigTypeTag()) { |
| 434 | .Void => return self.context().voidType(), |
| 435 | .NoReturn => return self.context().voidType(), |
| 436 | .Int => { |
| 437 | const info = t.intInfo(self.module.getTarget()); |
| 438 | return self.context().intType(info.bits); |
| 439 | }, |
| 440 | .Bool => return self.context().intType(1), |
| 441 | .Pointer => { |
| 442 | if (t.isSlice()) { |
| 443 | return self.fail(src, "TODO: LLVM backend: implement slices", .{}); |
| 444 | } else { |
| 445 | const elem_type = try self.getLLVMType(t.elemType(), src); |
| 446 | return elem_type.pointerType(0); |
| 447 | } |
| 448 | }, |
| 449 | .Array => { |
| 450 | const elem_type = try self.getLLVMType(t.elemType(), src); |
| 451 | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); |
| 452 | }, |
| 453 | .Optional => { |
| 454 | if (!t.isPtrLikeOptional()) { |
| 455 | var buf: Type.Payload.ElemType = undefined; |
| 456 | const child_type = t.optionalChild(&buf); |
| 457 | |
| 458 | var optional_types: [2]*const llvm.Type = .{ |
| 459 | try self.getLLVMType(child_type, src), |
| 460 | self.context().intType(1), |
| 461 | }; |
| 462 | return self.context().structType(&optional_types, 2, .False); |
| 463 | } else { |
| 464 | return self.fail(src, "TODO implement optional pointers as actual pointers", .{}); |
| 465 | } |
| 466 | }, |
| 467 | else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | // TODO: figure out a way to remove the FuncGen argument |
| 472 | fn genTypedValue(self: *DeclGen, src: LazySrcLoc, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value { |
| 473 | const llvm_type = try self.getLLVMType(tv.ty, src); |
| 474 | |
| 475 | if (tv.val.isUndef()) |
| 476 | return llvm_type.getUndef(); |
| 477 | |
| 478 | switch (tv.ty.zigTypeTag()) { |
| 479 | .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), |
| 480 | .Int => { |
| 481 | var bigint_space: Value.BigIntSpace = undefined; |
| 482 | const bigint = tv.val.toBigInt(&bigint_space); |
| 483 | |
| 484 | if (bigint.eqZero()) return llvm_type.constNull(); |
| 485 | |
| 486 | if (bigint.limbs.len != 1) { |
| 487 | return self.fail(src, "TODO implement bigger bigint", .{}); |
| 488 | } |
| 489 | const llvm_int = llvm_type.constInt(bigint.limbs[0], .False); |
| 490 | if (!bigint.positive) { |
| 491 | return llvm.constNeg(llvm_int); |
| 492 | } |
| 493 | return llvm_int; |
| 494 | }, |
| 495 | .Pointer => switch (tv.val.tag()) { |
| 496 | .decl_ref => { |
| 497 | const decl = tv.val.castTag(.decl_ref).?.data; |
| 498 | const val = try self.resolveGlobalDecl(decl, src); |
| 499 | |
| 500 | const usize_type = try self.getLLVMType(Type.initTag(.usize), src); |
| 501 | |
| 502 | // TODO: second index should be the index into the memory! |
| 503 | var indices: [2]*const llvm.Value = .{ |
| 504 | usize_type.constNull(), |
| 505 | usize_type.constNull(), |
| 506 | }; |
| 507 | |
| 508 | // TODO: consider using buildInBoundsGEP2 for opaque pointers |
| 509 | return fg.?.builder.buildInBoundsGEP(val, &indices, 2, ""); |
| 510 | }, |
| 511 | .ref_val => { |
| 512 | const elem_value = tv.val.castTag(.ref_val).?.data; |
| 513 | const elem_type = tv.ty.castPointer().?.data; |
| 514 | const alloca = fg.?.buildAlloca(try self.getLLVMType(elem_type, src)); |
| 515 | _ = fg.?.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }, fg), alloca); |
| 516 | return alloca; |
| 517 | }, |
| 518 | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), |
| 519 | }, |
| 520 | .Array => { |
| 521 | if (tv.val.castTag(.bytes)) |payload| { |
| 522 | const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { |
| 523 | if (sentinel.tag() == .zero) break :blk true; |
| 524 | return self.fail(src, "TODO handle other sentinel values", .{}); |
| 525 | } else false; |
| 526 | |
| 527 | return self.context().constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel)); |
| 528 | } else { |
| 529 | return self.fail(src, "TODO handle more array values", .{}); |
| 530 | } |
| 531 | }, |
| 532 | .Optional => { |
| 533 | if (!tv.ty.isPtrLikeOptional()) { |
| 534 | var buf: Type.Payload.ElemType = undefined; |
| 535 | const child_type = tv.ty.optionalChild(&buf); |
| 536 | const llvm_child_type = try self.getLLVMType(child_type, src); |
| 537 | |
| 538 | if (tv.val.tag() == .null_value) { |
| 539 | var optional_values: [2]*const llvm.Value = .{ |
| 540 | llvm_child_type.constNull(), |
| 541 | self.context().intType(1).constNull(), |
| 542 | }; |
| 543 | return self.context().constStruct(&optional_values, 2, .False); |
| 544 | } else { |
| 545 | var optional_values: [2]*const llvm.Value = .{ |
| 546 | try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }, fg), |
| 547 | self.context().intType(1).constAllOnes(), |
| 548 | }; |
| 549 | return self.context().constStruct(&optional_values, 2, .False); |
| 550 | } |
| 551 | } else { |
| 552 | return self.fail(src, "TODO implement const of optional pointer", .{}); |
| 553 | } |
| 554 | }, |
| 555 | else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | // Helper functions |
| 560 | fn addAttr(self: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { |
| 561 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); |
| 562 | assert(kind_id != 0); |
| 563 | const llvm_attr = self.context().createEnumAttribute(kind_id, 0); |
| 564 | val.addAttributeAtIndex(index, llvm_attr); |
| 565 | } |
| 566 | |
| 567 | fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void { |
| 568 | // TODO: improve this API, `addAttr(-1, attr_name)` |
| 569 | self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); |
| 570 | } |
| 571 | }; |
| 572 | |
| 573 | pub const FuncGen = struct { |
| 574 | dg: *DeclGen, |
| 575 | |
| 576 | builder: *const llvm.Builder, |
| 577 | |
| 578 | /// This stores the LLVM values used in a function, such that they can be |
| 579 | /// referred to in other instructions. This table is cleared before every function is generated. |
| 580 | /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks |
| 581 | /// in here, however if a block ends, the instructions can be thrown away. |
| 582 | func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value), |
| 583 | |
| 584 | /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. |
| 585 | args: []*const llvm.Value, |
| 586 | arg_index: usize, |
| 587 | |
| 588 | entry_block: *const llvm.BasicBlock, |
| 589 | /// This fields stores the last alloca instruction, such that we can append more alloca instructions |
| 590 | /// to the top of the function. |
| 591 | latest_alloca_inst: ?*const llvm.Value, |
| 592 | |
| 593 | llvm_func: *const llvm.Value, |
| 594 | |
| 595 | /// This data structure is used to implement breaking to blocks. |
| 596 | blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct { |
| 597 | parent_bb: *const llvm.BasicBlock, |
| 598 | break_bbs: *BreakBasicBlocks, |
| 599 | break_vals: *BreakValues, |
| 600 | }), |
| 601 | |
| 602 | const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); |
| 603 | const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); |
| 604 | |
| 605 | fn deinit(self: *FuncGen) void { |
| 606 | self.builder.dispose(); |
| 607 | self.func_inst_table.deinit(self.gpa()); |
| 608 | self.gpa().free(self.args); |
| 609 | self.blocks.deinit(self.gpa()); |
| 610 | } |
| 611 | |
| 612 | fn fail(self: *FuncGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 613 | @setCold(true); |
| 614 | return self.dg.fail(src, format, args); |
| 615 | } |
| 616 | |
| 617 | fn llvmModule(self: *FuncGen) *const llvm.Module { |
| 618 | return self.dg.object.llvm_module; |
| 619 | } |
| 620 | |
| 621 | fn context(self: *FuncGen) *const llvm.Context { |
| 622 | return self.dg.object.context; |
| 623 | } |
| 624 | |
| 625 | fn gpa(self: *FuncGen) *Allocator { |
| 626 | return self.dg.gpa; |
| 627 | } |
| 628 | |
| 629 | fn resolveInst(self: *FuncGen, inst: *ir.Inst) !*const llvm.Value { |
| 630 | if (inst.value()) |val| { |
| 631 | return self.dg.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val }, self); |
| 632 | } |
| 633 | if (self.func_inst_table.get(inst)) |value| return value; |
| 634 | |
| 635 | return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); |
| 636 | } |
| 637 | |
| 638 | fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { |
| 398 | 639 | for (body.instructions) |inst| { |
| 399 | 640 | const opt_value = switch (inst.tag) { |
| 400 | 641 | .add => try self.genAdd(inst.castTag(.add).?), |
| ... | ... | @@ -434,11 +675,11 @@ pub const LLVMIRModule = struct { |
| 434 | 675 | }, |
| 435 | 676 | else => |tag| return self.fail(inst.src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), |
| 436 | 677 | }; |
| 437 | | if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val); |
| 678 | if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa(), inst, val); |
| 438 | 679 | } |
| 439 | 680 | } |
| 440 | 681 | |
| 441 | | fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { |
| 682 | fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value { |
| 442 | 683 | if (inst.func.value()) |func_value| { |
| 443 | 684 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| |
| 444 | 685 | extern_fn.data |
| ... | ... | @@ -448,12 +689,12 @@ pub const LLVMIRModule = struct { |
| 448 | 689 | unreachable; |
| 449 | 690 | |
| 450 | 691 | const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty; |
| 451 | | const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src); |
| 692 | const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl, inst.base.src); |
| 452 | 693 | |
| 453 | 694 | const num_args = inst.args.len; |
| 454 | 695 | |
| 455 | | const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, num_args); |
| 456 | | defer self.gpa.free(llvm_param_vals); |
| 696 | const llvm_param_vals = try self.gpa().alloc(*const llvm.Value, num_args); |
| 697 | defer self.gpa().free(llvm_param_vals); |
| 457 | 698 | |
| 458 | 699 | for (inst.args) |arg, i| { |
| 459 | 700 | llvm_param_vals[i] = try self.resolveInst(arg); |
| ... | ... | @@ -482,17 +723,17 @@ pub const LLVMIRModule = struct { |
| 482 | 723 | } |
| 483 | 724 | } |
| 484 | 725 | |
| 485 | | fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { |
| 726 | fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { |
| 486 | 727 | _ = self.builder.buildRetVoid(); |
| 487 | 728 | return null; |
| 488 | 729 | } |
| 489 | 730 | |
| 490 | | fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { |
| 731 | fn genRet(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 491 | 732 | _ = self.builder.buildRet(try self.resolveInst(inst.operand)); |
| 492 | 733 | return null; |
| 493 | 734 | } |
| 494 | 735 | |
| 495 | | fn genCmp(self: *LLVMIRModule, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value { |
| 736 | fn genCmp(self: *FuncGen, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value { |
| 496 | 737 | const lhs = try self.resolveInst(inst.lhs); |
| 497 | 738 | const rhs = try self.resolveInst(inst.rhs); |
| 498 | 739 | |
| ... | ... | @@ -513,21 +754,21 @@ pub const LLVMIRModule = struct { |
| 513 | 754 | return self.builder.buildICmp(operation, lhs, rhs, ""); |
| 514 | 755 | } |
| 515 | 756 | |
| 516 | | fn genBlock(self: *LLVMIRModule, inst: *Inst.Block) !?*const llvm.Value { |
| 517 | | const parent_bb = self.context.createBasicBlock("Block"); |
| 757 | fn genBlock(self: *FuncGen, inst: *Inst.Block) !?*const llvm.Value { |
| 758 | const parent_bb = self.context().createBasicBlock("Block"); |
| 518 | 759 | |
| 519 | 760 | // 5 breaks to a block seems like a reasonable default. |
| 520 | | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5); |
| 521 | | var break_vals = try BreakValues.initCapacity(self.gpa, 5); |
| 522 | | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 761 | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa(), 5); |
| 762 | var break_vals = try BreakValues.initCapacity(self.gpa(), 5); |
| 763 | try self.blocks.putNoClobber(self.gpa(), inst, .{ |
| 523 | 764 | .parent_bb = parent_bb, |
| 524 | 765 | .break_bbs = &break_bbs, |
| 525 | 766 | .break_vals = &break_vals, |
| 526 | 767 | }); |
| 527 | 768 | defer { |
| 528 | 769 | self.blocks.removeAssertDiscard(inst); |
| 529 | | break_bbs.deinit(self.gpa); |
| 530 | | break_vals.deinit(self.gpa); |
| 770 | break_bbs.deinit(self.gpa()); |
| 771 | break_vals.deinit(self.gpa()); |
| 531 | 772 | } |
| 532 | 773 | |
| 533 | 774 | try self.genBody(inst.body); |
| ... | ... | @@ -538,7 +779,7 @@ pub const LLVMIRModule = struct { |
| 538 | 779 | // If the block does not return a value, we dont have to create a phi node. |
| 539 | 780 | if (!inst.base.ty.hasCodeGenBits()) return null; |
| 540 | 781 | |
| 541 | | const phi_node = self.builder.buildPhi(try self.getLLVMType(inst.base.ty, inst.base.src), ""); |
| 782 | const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst.base.ty, inst.base.src), ""); |
| 542 | 783 | phi_node.addIncoming( |
| 543 | 784 | break_vals.items.ptr, |
| 544 | 785 | break_bbs.items.ptr, |
| ... | ... | @@ -547,7 +788,7 @@ pub const LLVMIRModule = struct { |
| 547 | 788 | return phi_node; |
| 548 | 789 | } |
| 549 | 790 | |
| 550 | | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { |
| 791 | fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value { |
| 551 | 792 | var block = self.blocks.get(inst.block).?; |
| 552 | 793 | |
| 553 | 794 | // If the break doesn't break a value, then we don't have to add |
| ... | ... | @@ -560,25 +801,25 @@ pub const LLVMIRModule = struct { |
| 560 | 801 | |
| 561 | 802 | // For the phi node, we need the basic blocks and the values of the |
| 562 | 803 | // break instructions. |
| 563 | | try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); |
| 564 | | try block.break_vals.append(self.gpa, val); |
| 804 | try block.break_bbs.append(self.gpa(), self.builder.getInsertBlock()); |
| 805 | try block.break_vals.append(self.gpa(), val); |
| 565 | 806 | |
| 566 | 807 | _ = self.builder.buildBr(block.parent_bb); |
| 567 | 808 | } |
| 568 | 809 | return null; |
| 569 | 810 | } |
| 570 | 811 | |
| 571 | | fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value { |
| 812 | fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value { |
| 572 | 813 | var block = self.blocks.get(inst.block).?; |
| 573 | 814 | _ = self.builder.buildBr(block.parent_bb); |
| 574 | 815 | return null; |
| 575 | 816 | } |
| 576 | 817 | |
| 577 | | fn genCondBr(self: *LLVMIRModule, inst: *Inst.CondBr) !?*const llvm.Value { |
| 818 | fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value { |
| 578 | 819 | const condition_value = try self.resolveInst(inst.condition); |
| 579 | 820 | |
| 580 | | const then_block = self.context.appendBasicBlock(self.llvm_func, "Then"); |
| 581 | | const else_block = self.context.appendBasicBlock(self.llvm_func, "Else"); |
| 821 | const then_block = self.context().appendBasicBlock(self.llvm_func, "Then"); |
| 822 | const else_block = self.context().appendBasicBlock(self.llvm_func, "Else"); |
| 582 | 823 | { |
| 583 | 824 | const prev_block = self.builder.getInsertBlock(); |
| 584 | 825 | defer self.builder.positionBuilderAtEnd(prev_block); |
| ... | ... | @@ -593,8 +834,8 @@ pub const LLVMIRModule = struct { |
| 593 | 834 | return null; |
| 594 | 835 | } |
| 595 | 836 | |
| 596 | | fn genLoop(self: *LLVMIRModule, inst: *Inst.Loop) !?*const llvm.Value { |
| 597 | | const loop_block = self.context.appendBasicBlock(self.llvm_func, "Loop"); |
| 837 | fn genLoop(self: *FuncGen, inst: *Inst.Loop) !?*const llvm.Value { |
| 838 | const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop"); |
| 598 | 839 | _ = self.builder.buildBr(loop_block); |
| 599 | 840 | |
| 600 | 841 | self.builder.positionBuilderAtEnd(loop_block); |
| ... | ... | @@ -604,20 +845,20 @@ pub const LLVMIRModule = struct { |
| 604 | 845 | return null; |
| 605 | 846 | } |
| 606 | 847 | |
| 607 | | fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { |
| 848 | fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 608 | 849 | return self.builder.buildNot(try self.resolveInst(inst.operand), ""); |
| 609 | 850 | } |
| 610 | 851 | |
| 611 | | fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { |
| 852 | fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { |
| 612 | 853 | _ = self.builder.buildUnreachable(); |
| 613 | 854 | return null; |
| 614 | 855 | } |
| 615 | 856 | |
| 616 | | fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 857 | fn genIsNonNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 617 | 858 | const operand = try self.resolveInst(inst.operand); |
| 618 | 859 | |
| 619 | 860 | if (operand_is_ptr) { |
| 620 | | const index_type = self.context.intType(32); |
| 861 | const index_type = self.context().intType(32); |
| 621 | 862 | |
| 622 | 863 | var indices: [2]*const llvm.Value = .{ |
| 623 | 864 | index_type.constNull(), |
| ... | ... | @@ -630,15 +871,15 @@ pub const LLVMIRModule = struct { |
| 630 | 871 | } |
| 631 | 872 | } |
| 632 | 873 | |
| 633 | | fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 874 | fn genIsNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 634 | 875 | return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, ""); |
| 635 | 876 | } |
| 636 | 877 | |
| 637 | | fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 878 | fn genOptionalPayload(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { |
| 638 | 879 | const operand = try self.resolveInst(inst.operand); |
| 639 | 880 | |
| 640 | 881 | if (operand_is_ptr) { |
| 641 | | const index_type = self.context.intType(32); |
| 882 | const index_type = self.context().intType(32); |
| 642 | 883 | |
| 643 | 884 | var indices: [2]*const llvm.Value = .{ |
| 644 | 885 | index_type.constNull(), |
| ... | ... | @@ -651,7 +892,7 @@ pub const LLVMIRModule = struct { |
| 651 | 892 | } |
| 652 | 893 | } |
| 653 | 894 | |
| 654 | | fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { |
| 895 | fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { |
| 655 | 896 | const lhs = try self.resolveInst(inst.lhs); |
| 656 | 897 | const rhs = try self.resolveInst(inst.rhs); |
| 657 | 898 | |
| ... | ... | @@ -664,7 +905,7 @@ pub const LLVMIRModule = struct { |
| 664 | 905 | self.builder.buildNUWAdd(lhs, rhs, ""); |
| 665 | 906 | } |
| 666 | 907 | |
| 667 | | fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { |
| 908 | fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { |
| 668 | 909 | const lhs = try self.resolveInst(inst.lhs); |
| 669 | 910 | const rhs = try self.resolveInst(inst.rhs); |
| 670 | 911 | |
| ... | ... | @@ -677,44 +918,44 @@ pub const LLVMIRModule = struct { |
| 677 | 918 | self.builder.buildNUWSub(lhs, rhs, ""); |
| 678 | 919 | } |
| 679 | 920 | |
| 680 | | fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { |
| 921 | fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 681 | 922 | const val = try self.resolveInst(inst.operand); |
| 682 | 923 | |
| 683 | 924 | const signed = inst.base.ty.isSignedInt(); |
| 684 | 925 | // TODO: Should we use intcast here or just a simple bitcast? |
| 685 | 926 | // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes |
| 686 | | return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), ""); |
| 927 | return self.builder.buildIntCast2(val, try self.dg.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), ""); |
| 687 | 928 | } |
| 688 | 929 | |
| 689 | | fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { |
| 930 | fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 690 | 931 | const val = try self.resolveInst(inst.operand); |
| 691 | | const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src); |
| 932 | const dest_type = try self.dg.getLLVMType(inst.base.ty, inst.base.src); |
| 692 | 933 | |
| 693 | 934 | return self.builder.buildBitCast(val, dest_type, ""); |
| 694 | 935 | } |
| 695 | 936 | |
| 696 | | fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.Value { |
| 937 | fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value { |
| 697 | 938 | const arg_val = self.args[self.arg_index]; |
| 698 | 939 | self.arg_index += 1; |
| 699 | 940 | |
| 700 | | const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src)); |
| 941 | const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst.base.ty, inst.base.src)); |
| 701 | 942 | _ = self.builder.buildStore(arg_val, ptr_val); |
| 702 | 943 | return self.builder.buildLoad(ptr_val, ""); |
| 703 | 944 | } |
| 704 | 945 | |
| 705 | | fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { |
| 946 | fn genAlloc(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { |
| 706 | 947 | // buildAlloca expects the pointee type, not the pointer type, so assert that |
| 707 | 948 | // a Payload.PointerSimple is passed to the alloc instruction. |
| 708 | 949 | const pointee_type = inst.base.ty.castPointer().?.data; |
| 709 | 950 | |
| 710 | 951 | // TODO: figure out a way to get the name of the var decl. |
| 711 | 952 | // TODO: set alignment and volatile |
| 712 | | return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src)); |
| 953 | return self.buildAlloca(try self.dg.getLLVMType(pointee_type, inst.base.src)); |
| 713 | 954 | } |
| 714 | 955 | |
| 715 | 956 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 716 | 957 | /// put the alloca instruction at the top of the function! |
| 717 | | fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value { |
| 958 | fn buildAlloca(self: *FuncGen, t: *const llvm.Type) *const llvm.Value { |
| 718 | 959 | const prev_block = self.builder.getInsertBlock(); |
| 719 | 960 | defer self.builder.positionBuilderAtEnd(prev_block); |
| 720 | 961 | |
| ... | ... | @@ -736,240 +977,30 @@ pub const LLVMIRModule = struct { |
| 736 | 977 | return val; |
| 737 | 978 | } |
| 738 | 979 | |
| 739 | | fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { |
| 980 | fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { |
| 740 | 981 | const val = try self.resolveInst(inst.rhs); |
| 741 | 982 | const ptr = try self.resolveInst(inst.lhs); |
| 742 | 983 | _ = self.builder.buildStore(val, ptr); |
| 743 | 984 | return null; |
| 744 | 985 | } |
| 745 | 986 | |
| 746 | | fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { |
| 987 | fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { |
| 747 | 988 | const ptr_val = try self.resolveInst(inst.operand); |
| 748 | 989 | return self.builder.buildLoad(ptr_val, ""); |
| 749 | 990 | } |
| 750 | 991 | |
| 751 | | fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { |
| 992 | fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { |
| 752 | 993 | const llvn_fn = self.getIntrinsic("llvm.debugtrap"); |
| 753 | 994 | _ = self.builder.buildCall(llvn_fn, null, 0, ""); |
| 754 | 995 | return null; |
| 755 | 996 | } |
| 756 | 997 | |
| 757 | | fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.Value { |
| 998 | fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { |
| 758 | 999 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); |
| 759 | 1000 | assert(id != 0); |
| 760 | 1001 | // TODO: add support for overload intrinsics by passing the prefix of the intrinsic |
| 761 | 1002 | // to `lookupIntrinsicID` and then passing the correct types to |
| 762 | 1003 | // `getIntrinsicDeclaration` |
| 763 | | return self.llvm_module.getIntrinsicDeclaration(id, null, 0); |
| 764 | | } |
| 765 | | |
| 766 | | fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.Value { |
| 767 | | if (inst.value()) |val| { |
| 768 | | return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val }); |
| 769 | | } |
| 770 | | if (self.func_inst_table.get(inst)) |value| return value; |
| 771 | | |
| 772 | | return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); |
| 773 | | } |
| 774 | | |
| 775 | | fn genTypedValue(self: *LLVMIRModule, src: LazySrcLoc, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value { |
| 776 | | const llvm_type = try self.getLLVMType(tv.ty, src); |
| 777 | | |
| 778 | | if (tv.val.isUndef()) |
| 779 | | return llvm_type.getUndef(); |
| 780 | | |
| 781 | | switch (tv.ty.zigTypeTag()) { |
| 782 | | .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), |
| 783 | | .Int => { |
| 784 | | var bigint_space: Value.BigIntSpace = undefined; |
| 785 | | const bigint = tv.val.toBigInt(&bigint_space); |
| 786 | | |
| 787 | | if (bigint.eqZero()) return llvm_type.constNull(); |
| 788 | | |
| 789 | | if (bigint.limbs.len != 1) { |
| 790 | | return self.fail(src, "TODO implement bigger bigint", .{}); |
| 791 | | } |
| 792 | | const llvm_int = llvm_type.constInt(bigint.limbs[0], .False); |
| 793 | | if (!bigint.positive) { |
| 794 | | return llvm.constNeg(llvm_int); |
| 795 | | } |
| 796 | | return llvm_int; |
| 797 | | }, |
| 798 | | .Pointer => switch (tv.val.tag()) { |
| 799 | | .decl_ref => { |
| 800 | | const decl = tv.val.castTag(.decl_ref).?.data; |
| 801 | | const val = try self.resolveGlobalDecl(decl, src); |
| 802 | | |
| 803 | | const usize_type = try self.getLLVMType(Type.initTag(.usize), src); |
| 804 | | |
| 805 | | // TODO: second index should be the index into the memory! |
| 806 | | var indices: [2]*const llvm.Value = .{ |
| 807 | | usize_type.constNull(), |
| 808 | | usize_type.constNull(), |
| 809 | | }; |
| 810 | | |
| 811 | | // TODO: consider using buildInBoundsGEP2 for opaque pointers |
| 812 | | return self.builder.buildInBoundsGEP(val, &indices, 2, ""); |
| 813 | | }, |
| 814 | | .ref_val => { |
| 815 | | const elem_value = tv.val.castTag(.ref_val).?.data; |
| 816 | | const elem_type = tv.ty.castPointer().?.data; |
| 817 | | const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src)); |
| 818 | | _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca); |
| 819 | | return alloca; |
| 820 | | }, |
| 821 | | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), |
| 822 | | }, |
| 823 | | .Array => { |
| 824 | | if (tv.val.castTag(.bytes)) |payload| { |
| 825 | | const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { |
| 826 | | if (sentinel.tag() == .zero) break :blk true; |
| 827 | | return self.fail(src, "TODO handle other sentinel values", .{}); |
| 828 | | } else false; |
| 829 | | |
| 830 | | return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel)); |
| 831 | | } else { |
| 832 | | return self.fail(src, "TODO handle more array values", .{}); |
| 833 | | } |
| 834 | | }, |
| 835 | | .Optional => { |
| 836 | | if (!tv.ty.isPtrLikeOptional()) { |
| 837 | | var buf: Type.Payload.ElemType = undefined; |
| 838 | | const child_type = tv.ty.optionalChild(&buf); |
| 839 | | const llvm_child_type = try self.getLLVMType(child_type, src); |
| 840 | | |
| 841 | | if (tv.val.tag() == .null_value) { |
| 842 | | var optional_values: [2]*const llvm.Value = .{ |
| 843 | | llvm_child_type.constNull(), |
| 844 | | self.context.intType(1).constNull(), |
| 845 | | }; |
| 846 | | return self.context.constStruct(&optional_values, 2, .False); |
| 847 | | } else { |
| 848 | | var optional_values: [2]*const llvm.Value = .{ |
| 849 | | try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }), |
| 850 | | self.context.intType(1).constAllOnes(), |
| 851 | | }; |
| 852 | | return self.context.constStruct(&optional_values, 2, .False); |
| 853 | | } |
| 854 | | } else { |
| 855 | | return self.fail(src, "TODO implement const of optional pointer", .{}); |
| 856 | | } |
| 857 | | }, |
| 858 | | else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), |
| 859 | | } |
| 860 | | } |
| 861 | | |
| 862 | | fn getLLVMType(self: *LLVMIRModule, t: Type, src: LazySrcLoc) error{ OutOfMemory, CodegenFail }!*const llvm.Type { |
| 863 | | switch (t.zigTypeTag()) { |
| 864 | | .Void => return self.context.voidType(), |
| 865 | | .NoReturn => return self.context.voidType(), |
| 866 | | .Int => { |
| 867 | | const info = t.intInfo(self.module.getTarget()); |
| 868 | | return self.context.intType(info.bits); |
| 869 | | }, |
| 870 | | .Bool => return self.context.intType(1), |
| 871 | | .Pointer => { |
| 872 | | if (t.isSlice()) { |
| 873 | | return self.fail(src, "TODO: LLVM backend: implement slices", .{}); |
| 874 | | } else { |
| 875 | | const elem_type = try self.getLLVMType(t.elemType(), src); |
| 876 | | return elem_type.pointerType(0); |
| 877 | | } |
| 878 | | }, |
| 879 | | .Array => { |
| 880 | | const elem_type = try self.getLLVMType(t.elemType(), src); |
| 881 | | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); |
| 882 | | }, |
| 883 | | .Optional => { |
| 884 | | if (!t.isPtrLikeOptional()) { |
| 885 | | var buf: Type.Payload.ElemType = undefined; |
| 886 | | const child_type = t.optionalChild(&buf); |
| 887 | | |
| 888 | | var optional_types: [2]*const llvm.Type = .{ |
| 889 | | try self.getLLVMType(child_type, src), |
| 890 | | self.context.intType(1), |
| 891 | | }; |
| 892 | | return self.context.structType(&optional_types, 2, .False); |
| 893 | | } else { |
| 894 | | return self.fail(src, "TODO implement optional pointers as actual pointers", .{}); |
| 895 | | } |
| 896 | | }, |
| 897 | | else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), |
| 898 | | } |
| 899 | | } |
| 900 | | |
| 901 | | fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: LazySrcLoc) error{ OutOfMemory, CodegenFail }!*const llvm.Value { |
| 902 | | // TODO: do we want to store this in our own datastructure? |
| 903 | | if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val; |
| 904 | | |
| 905 | | const typed_value = decl.typed_value.most_recent.typed_value; |
| 906 | | |
| 907 | | // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`. |
| 908 | | const llvm_type = try self.getLLVMType(typed_value.ty, src); |
| 909 | | const val = try self.genTypedValue(src, typed_value); |
| 910 | | const global = self.llvm_module.addGlobal(llvm_type, decl.name); |
| 911 | | llvm.setInitializer(global, val); |
| 912 | | |
| 913 | | // TODO ask the Decl if it is const |
| 914 | | // https://github.com/ziglang/zig/issues/7582 |
| 915 | | |
| 916 | | return global; |
| 917 | | } |
| 918 | | |
| 919 | | /// If the llvm function does not exist, create it |
| 920 | | fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: LazySrcLoc) !*const llvm.Value { |
| 921 | | // TODO: do we want to store this in our own datastructure? |
| 922 | | if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn; |
| 923 | | |
| 924 | | const zig_fn_type = func.typed_value.most_recent.typed_value.ty; |
| 925 | | const return_type = zig_fn_type.fnReturnType(); |
| 926 | | |
| 927 | | const fn_param_len = zig_fn_type.fnParamLen(); |
| 928 | | |
| 929 | | const fn_param_types = try self.gpa.alloc(Type, fn_param_len); |
| 930 | | defer self.gpa.free(fn_param_types); |
| 931 | | zig_fn_type.fnParamTypes(fn_param_types); |
| 932 | | |
| 933 | | const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len); |
| 934 | | defer self.gpa.free(llvm_param); |
| 935 | | |
| 936 | | for (fn_param_types) |fn_param, i| { |
| 937 | | llvm_param[i] = try self.getLLVMType(fn_param, src); |
| 938 | | } |
| 939 | | |
| 940 | | const fn_type = llvm.Type.functionType( |
| 941 | | try self.getLLVMType(return_type, src), |
| 942 | | if (fn_param_len == 0) null else llvm_param.ptr, |
| 943 | | @intCast(c_uint, fn_param_len), |
| 944 | | .False, |
| 945 | | ); |
| 946 | | const llvm_fn = self.llvm_module.addFunction(func.name, fn_type); |
| 947 | | |
| 948 | | if (return_type.tag() == .noreturn) { |
| 949 | | self.addFnAttr(llvm_fn, "noreturn"); |
| 950 | | } |
| 951 | | |
| 952 | | return llvm_fn; |
| 953 | | } |
| 954 | | |
| 955 | | // Helper functions |
| 956 | | fn addAttr(self: LLVMIRModule, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { |
| 957 | | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); |
| 958 | | assert(kind_id != 0); |
| 959 | | const llvm_attr = self.context.createEnumAttribute(kind_id, 0); |
| 960 | | val.addAttributeAtIndex(index, llvm_attr); |
| 961 | | } |
| 962 | | |
| 963 | | fn addFnAttr(self: *LLVMIRModule, val: *const llvm.Value, attr_name: []const u8) void { |
| 964 | | // TODO: improve this API, `addAttr(-1, attr_name)` |
| 965 | | self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); |
| 966 | | } |
| 967 | | |
| 968 | | pub fn fail(self: *LLVMIRModule, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 969 | | @setCold(true); |
| 970 | | assert(self.err_msg == null); |
| 971 | | const src_loc = src.toSrcLocWithDecl(self.decl); |
| 972 | | self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, format, args); |
| 973 | | return error.CodegenFail; |
| 1004 | return self.llvmModule().getIntrinsicDeclaration(id, null, 0); |
| 974 | 1005 | } |
| 975 | 1006 | }; |