authorgravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-03 16:00:12+01:00
committergravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-03 17:39:30+01:00
log0008bef1e643c190a12e13d99a21d5af7ebdaa1b
treef525e9fa6d4bbf431ac047ebc068399f4843b322
parente095ebf31254bf5eeba9c07a2ad724e880626f01

stage2: add support for integers in LLVM backend

Also adds support for simple operators, like add and subtract. The intcast and bitcast instruction also have been implemented. Linking with libc also works, so we can now generate working executables! `zig build-exe example.zig -fLLVM -lc`: ``` fn add(a: i32, b: i32) i32 { return a + b; } export fn main() c_int { var a: i32 = -5; const x = add(a, 7); var y = add(2, 0); y -= x; return y; } ```

2 files changed, 152 insertions(+), 60 deletions(-)

src/llvm_backend.zig+125-60
...@@ -288,59 +288,62 @@ pub const LLVMIRModule = struct {...@@ -288,59 +288,62 @@ pub const LLVMIRModule = struct {
288 }288 }
289289
290 fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {290 fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {
291 switch (typed_value.ty.zigTypeTag()) {291 if (typed_value.val.castTag(.function)) |func_inst| {
292 .Fn => {292 const func = func_inst.data;
293 const func = typed_value.val.castTag(.function).?.data;
294293
295 const llvm_func = try self.resolveLLVMFunction(func, src);294 const llvm_func = try self.resolveLLVMFunction(func, src);
296295
297 // This gets the LLVM values from the function and stores them in `self.args`.296 // This gets the LLVM values from the function and stores them in `self.args`.
298 const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();297 const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();
299 var args = try self.gpa.alloc(*const llvm.ValueRef, fn_param_len);298 var args = try self.gpa.alloc(*const llvm.ValueRef, fn_param_len);
300 defer self.gpa.free(args);299 defer self.gpa.free(args);
301300
302 for (args) |*arg, i| {301 for (args) |*arg, i| {
303 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));302 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
304 }303 }
305 self.args = args;304 self.args = args;
306 self.arg_index = 0;305 self.arg_index = 0;
307306
308 // Make sure no other LLVM values from other functions can be referenced307 // Make sure no other LLVM values from other functions can be referenced
309 self.func_inst_table.clearRetainingCapacity();308 self.func_inst_table.clearRetainingCapacity();
310309
311 // We remove all the basic blocks of a function to support incremental310 // We remove all the basic blocks of a function to support incremental
312 // compilation!311 // compilation!
313 // TODO: remove all basic blocks if functions can have more than one312 // TODO: remove all basic blocks if functions can have more than one
314 if (llvm_func.getFirstBasicBlock()) |bb| {313 if (llvm_func.getFirstBasicBlock()) |bb| {
315 bb.deleteBasicBlock();314 bb.deleteBasicBlock();
316 }315 }
317316
318 const entry_block = llvm_func.appendBasicBlock("Entry");317 const entry_block = llvm_func.appendBasicBlock("Entry");
319 self.builder.positionBuilderAtEnd(entry_block);318 self.builder.positionBuilderAtEnd(entry_block);
320319
321 const instructions = func.body.instructions;320 const instructions = func.body.instructions;
322 for (instructions) |inst| {321 for (instructions) |inst| {
323 const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {322 const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {
324 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),323 .add => try self.genAdd(inst.castTag(.add).?),
325 .call => try self.genCall(inst.castTag(.call).?),324 .alloc => try self.genAlloc(inst.castTag(.alloc).?),
326 .unreach => self.genUnreach(inst.castTag(.unreach).?),325 .arg => try self.genArg(inst.castTag(.arg).?),
327 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),326 .bitcast => try self.genBitCast(inst.castTag(.bitcast).?),
328 .arg => try self.genArg(inst.castTag(.arg).?),327 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
329 .alloc => try self.genAlloc(inst.castTag(.alloc).?),328 .call => try self.genCall(inst.castTag(.call).?),
330 .store => try self.genStore(inst.castTag(.store).?),329 .intcast => try self.genIntCast(inst.castTag(.intcast).?),
331 .load => try self.genLoad(inst.castTag(.load).?),330 .load => try self.genLoad(inst.castTag(.load).?),
332 .ret => try self.genRet(inst.castTag(.ret).?),331 .not => try self.genNot(inst.castTag(.not).?),
333 .not => try self.genNot(inst.castTag(.not).?),332 .ret => try self.genRet(inst.castTag(.ret).?),
334 .dbg_stmt => blk: {333 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
335 // TODO: implement debug info334 .store => try self.genStore(inst.castTag(.store).?),
336 break :blk null;335 .sub => try self.genSub(inst.castTag(.sub).?),
337 },336 .unreach => self.genUnreach(inst.castTag(.unreach).?),
338 else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),337 .dbg_stmt => blk: {
339 };338 // TODO: implement debug info
340 if (opt_llvm_val) |llvm_val| try self.func_inst_table.put(self.gpa, inst, llvm_val);339 break :blk null;
341 }340 },
342 },341 else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
343 else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),342 };
343 if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val);
344 }
345 } else {
346 return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty});
344 }347 }
345 }348 }
346349
...@@ -369,13 +372,13 @@ pub const LLVMIRModule = struct {...@@ -369,13 +372,13 @@ pub const LLVMIRModule = struct {
369 "",372 "",
370 );373 );
371374
372 const return_type = zig_fn_type.fnReturnType().zigTypeTag();375 const return_type = zig_fn_type.fnReturnType();
373 if (return_type == .NoReturn) {376 if (return_type.tag() == .noreturn) {
374 _ = self.builder.buildUnreachable();377 _ = self.builder.buildUnreachable();
375 }378 }
376379
377 // No need to store the LLVM value if the return type is void or noreturn380 // No need to store the LLVM value if the return type is void or noreturn
378 if (return_type == .NoReturn or return_type == .Void) return null;381 if (!return_type.hasCodeGenBits()) return null;
379382
380 return call;383 return call;
381 }384 }
...@@ -402,6 +405,48 @@ pub const LLVMIRModule = struct {...@@ -402,6 +405,48 @@ pub const LLVMIRModule = struct {
402 return null;405 return null;
403 }406 }
404407
408 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
409 const lhs = try self.resolveInst(inst.lhs);
410 const rhs = try self.resolveInst(inst.rhs);
411
412 if (!inst.base.ty.isInt())
413 return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty});
414
415 return if (inst.base.ty.isSignedInt())
416 self.builder.buildNSWAdd(lhs, rhs, "")
417 else
418 self.builder.buildNUWAdd(lhs, rhs, "");
419 }
420
421 fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
422 const lhs = try self.resolveInst(inst.lhs);
423 const rhs = try self.resolveInst(inst.rhs);
424
425 if (!inst.base.ty.isInt())
426 return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty});
427
428 return if (inst.base.ty.isSignedInt())
429 self.builder.buildNSWSub(lhs, rhs, "")
430 else
431 self.builder.buildNUWSub(lhs, rhs, "");
432 }
433
434 fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
435 const val = try self.resolveInst(inst.operand);
436
437 const signed = inst.base.ty.isSignedInt();
438 // TODO: Should we use intcast here or just a simple bitcast?
439 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
440 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, "");
441 }
442
443 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
444 const val = try self.resolveInst(inst.operand);
445 const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src);
446
447 return self.builder.buildBitCast(val, dest_type, "");
448 }
449
405 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef {450 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef {
406 const arg_val = self.args[self.arg_index];451 const arg_val = self.args[self.arg_index];
407 self.arg_index += 1;452 self.arg_index += 1;
...@@ -449,23 +494,38 @@ pub const LLVMIRModule = struct {...@@ -449,23 +494,38 @@ pub const LLVMIRModule = struct {
449 }494 }
450495
451 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {496 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {
452 if (inst.castTag(.constant)) |const_inst| {497 if (inst.value()) |val| {
453 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });498 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val });
454 }499 }
455 if (self.func_inst_table.get(inst)) |value| return value;500 if (self.func_inst_table.get(inst)) |value| return value;
456501
457 return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});502 return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
458 }503 }
459504
460 fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef {505 fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) !*const llvm.ValueRef {
461 const llvm_type = try self.getLLVMType(typed_value.ty, src);506 const llvm_type = try self.getLLVMType(tv.ty, src);
462507
463 if (typed_value.val.isUndef())508 if (tv.val.isUndef())
464 return llvm_type.getUndef();509 return llvm_type.getUndef();
465510
466 switch (typed_value.ty.zigTypeTag()) {511 switch (tv.ty.zigTypeTag()) {
467 .Bool => return if (typed_value.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),512 .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
468 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),513 .Int => {
514 var bigint_space: Value.BigIntSpace = undefined;
515 const bigint = tv.val.toBigInt(&bigint_space);
516
517 if (bigint.eqZero()) return llvm_type.constNull();
518
519 if (bigint.limbs.len != 1) {
520 return self.fail(src, "TODO implement bigger bigint", .{});
521 }
522 const llvm_int = llvm_type.constInt(bigint.limbs[0], false);
523 if (!bigint.positive) {
524 return llvm.constNeg(llvm_int);
525 }
526 return llvm_int;
527 },
528 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
469 }529 }
470 }530 }
471531
...@@ -498,14 +558,14 @@ pub const LLVMIRModule = struct {...@@ -498,14 +558,14 @@ pub const LLVMIRModule = struct {
498 );558 );
499 const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type);559 const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type);
500560
501 if (return_type.zigTypeTag() == .NoReturn) {561 if (return_type.tag() == .noreturn) {
502 llvm_fn.addFnAttr("noreturn");562 llvm_fn.addFnAttr("noreturn");
503 }563 }
504564
505 return llvm_fn;565 return llvm_fn;
506 }566 }
507567
508 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) !*const llvm.TypeRef {568 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef {
509 switch (t.zigTypeTag()) {569 switch (t.zigTypeTag()) {
510 .Void => return llvm.voidType(),570 .Void => return llvm.voidType(),
511 .NoReturn => return llvm.voidType(),571 .NoReturn => return llvm.voidType(),
...@@ -514,6 +574,11 @@ pub const LLVMIRModule = struct {...@@ -514,6 +574,11 @@ pub const LLVMIRModule = struct {
514 return llvm.intType(info.bits);574 return llvm.intType(info.bits);
515 },575 },
516 .Bool => return llvm.intType(1),576 .Bool => return llvm.intType(1),
577 .Pointer => {
578 const pointer = t.castPointer().?;
579 const elem_type = try self.getLLVMType(pointer.data, src);
580 return elem_type.pointerType(0);
581 },
517 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),582 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
518 }583 }
519 }584 }
src/llvm_bindings.zig+27
...@@ -43,8 +43,14 @@ pub const TypeRef = opaque {...@@ -43,8 +43,14 @@ pub const TypeRef = opaque {
43 pub const constAllOnes = LLVMConstAllOnes;43 pub const constAllOnes = LLVMConstAllOnes;
44 extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef;44 extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef;
4545
46 pub const constInt = LLVMConstInt;
47 extern fn LLVMConstInt(IntTy: *const TypeRef, N: c_ulonglong, SignExtend: LLVMBool) *const ValueRef;
48
46 pub const getUndef = LLVMGetUndef;49 pub const getUndef = LLVMGetUndef;
47 extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef;50 extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef;
51
52 pub const pointerType = LLVMPointerType;
53 extern fn LLVMPointerType(ElementType: *const TypeRef, AddressSpace: c_uint) *const TypeRef;
48};54};
4955
50pub const ModuleRef = opaque {56pub const ModuleRef = opaque {
...@@ -82,6 +88,9 @@ pub const VerifierFailureAction = extern enum {...@@ -82,6 +88,9 @@ pub const VerifierFailureAction = extern enum {
82 ReturnStatus,88 ReturnStatus,
83};89};
8490
91pub const constNeg = LLVMConstNeg;
92extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef;
93
85pub const voidType = LLVMVoidType;94pub const voidType = LLVMVoidType;
86extern fn LLVMVoidType() *const TypeRef;95extern fn LLVMVoidType() *const TypeRef;
8796
...@@ -143,6 +152,24 @@ pub const BuilderRef = opaque {...@@ -143,6 +152,24 @@ pub const BuilderRef = opaque {
143152
144 pub const buildNot = LLVMBuildNot;153 pub const buildNot = LLVMBuildNot;
145 extern fn LLVMBuildNot(*const BuilderRef, V: *const ValueRef, Name: [*:0]const u8) *const ValueRef;154 extern fn LLVMBuildNot(*const BuilderRef, V: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
155
156 pub const buildNSWAdd = LLVMBuildNSWAdd;
157 extern fn LLVMBuildNSWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
158
159 pub const buildNUWAdd = LLVMBuildNUWAdd;
160 extern fn LLVMBuildNUWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
161
162 pub const buildNSWSub = LLVMBuildNSWSub;
163 extern fn LLVMBuildNSWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
164
165 pub const buildNUWSub = LLVMBuildNUWSub;
166 extern fn LLVMBuildNUWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
167
168 pub const buildIntCast2 = LLVMBuildIntCast2;
169 extern fn LLVMBuildIntCast2(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, IsSigned: LLVMBool, Name: [*:0]const u8) *const ValueRef;
170
171 pub const buildBitCast = LLVMBuildBitCast;
172 extern fn LLVMBuildBitCast(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
146};173};
147174
148pub const BasicBlockRef = opaque {175pub const BasicBlockRef = opaque {