Skip to content

Commit 0008bef

Browse files
committed
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; } ```
1 parent e095ebf commit 0008bef

File tree

2 files changed

+152
-60
lines changed

2 files changed

+152
-60
lines changed

src/llvm_backend.zig

Lines changed: 125 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -288,59 +288,62 @@ pub const LLVMIRModule = struct {
288288
}
289289

290290
fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {
291-
switch (typed_value.ty.zigTypeTag()) {
292-
.Fn => {
293-
const func = typed_value.val.castTag(.function).?.data;
291+
if (typed_value.val.castTag(.function)) |func_inst| {
292+
const func = func_inst.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`.
298-
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);
300-
defer self.gpa.free(args);
296+
// This gets the LLVM values from the function and stores them in `self.args`.
297+
const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();
298+
var args = try self.gpa.alloc(*const llvm.ValueRef, fn_param_len);
299+
defer self.gpa.free(args);
301300

302-
for (args) |*arg, i| {
303-
arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
304-
}
305-
self.args = args;
306-
self.arg_index = 0;
301+
for (args) |*arg, i| {
302+
arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
303+
}
304+
self.args = args;
305+
self.arg_index = 0;
307306

308-
// Make sure no other LLVM values from other functions can be referenced
309-
self.func_inst_table.clearRetainingCapacity();
307+
// Make sure no other LLVM values from other functions can be referenced
308+
self.func_inst_table.clearRetainingCapacity();
310309

311-
// We remove all the basic blocks of a function to support incremental
312-
// compilation!
313-
// TODO: remove all basic blocks if functions can have more than one
314-
if (llvm_func.getFirstBasicBlock()) |bb| {
315-
bb.deleteBasicBlock();
316-
}
310+
// We remove all the basic blocks of a function to support incremental
311+
// compilation!
312+
// TODO: remove all basic blocks if functions can have more than one
313+
if (llvm_func.getFirstBasicBlock()) |bb| {
314+
bb.deleteBasicBlock();
315+
}
317316

318-
const entry_block = llvm_func.appendBasicBlock("Entry");
319-
self.builder.positionBuilderAtEnd(entry_block);
320-
321-
const instructions = func.body.instructions;
322-
for (instructions) |inst| {
323-
const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {
324-
.breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
325-
.call => try self.genCall(inst.castTag(.call).?),
326-
.unreach => self.genUnreach(inst.castTag(.unreach).?),
327-
.retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
328-
.arg => try self.genArg(inst.castTag(.arg).?),
329-
.alloc => try self.genAlloc(inst.castTag(.alloc).?),
330-
.store => try self.genStore(inst.castTag(.store).?),
331-
.load => try self.genLoad(inst.castTag(.load).?),
332-
.ret => try self.genRet(inst.castTag(.ret).?),
333-
.not => try self.genNot(inst.castTag(.not).?),
334-
.dbg_stmt => blk: {
335-
// TODO: implement debug info
336-
break :blk null;
337-
},
338-
else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
339-
};
340-
if (opt_llvm_val) |llvm_val| try self.func_inst_table.put(self.gpa, inst, llvm_val);
341-
}
342-
},
343-
else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),
317+
const entry_block = llvm_func.appendBasicBlock("Entry");
318+
self.builder.positionBuilderAtEnd(entry_block);
319+
320+
const instructions = func.body.instructions;
321+
for (instructions) |inst| {
322+
const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {
323+
.add => try self.genAdd(inst.castTag(.add).?),
324+
.alloc => try self.genAlloc(inst.castTag(.alloc).?),
325+
.arg => try self.genArg(inst.castTag(.arg).?),
326+
.bitcast => try self.genBitCast(inst.castTag(.bitcast).?),
327+
.breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
328+
.call => try self.genCall(inst.castTag(.call).?),
329+
.intcast => try self.genIntCast(inst.castTag(.intcast).?),
330+
.load => try self.genLoad(inst.castTag(.load).?),
331+
.not => try self.genNot(inst.castTag(.not).?),
332+
.ret => try self.genRet(inst.castTag(.ret).?),
333+
.retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
334+
.store => try self.genStore(inst.castTag(.store).?),
335+
.sub => try self.genSub(inst.castTag(.sub).?),
336+
.unreach => self.genUnreach(inst.castTag(.unreach).?),
337+
.dbg_stmt => blk: {
338+
// TODO: implement debug info
339+
break :blk null;
340+
},
341+
else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
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});
344347
}
345348
}
346349

@@ -369,13 +372,13 @@ pub const LLVMIRModule = struct {
369372
"",
370373
);
371374

372-
const return_type = zig_fn_type.fnReturnType().zigTypeTag();
373-
if (return_type == .NoReturn) {
375+
const return_type = zig_fn_type.fnReturnType();
376+
if (return_type.tag() == .noreturn) {
374377
_ = self.builder.buildUnreachable();
375378
}
376379

377380
// 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

380383
return call;
381384
}
@@ -402,6 +405,48 @@ pub const LLVMIRModule = struct {
402405
return null;
403406
}
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+
405450
fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef {
406451
const arg_val = self.args[self.arg_index];
407452
self.arg_index += 1;
@@ -449,23 +494,38 @@ pub const LLVMIRModule = struct {
449494
}
450495

451496
fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {
452-
if (inst.castTag(.constant)) |const_inst| {
453-
return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
497+
if (inst.value()) |val| {
498+
return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val });
454499
}
455500
if (self.func_inst_table.get(inst)) |value| return value;
456501

457502
return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
458503
}
459504

460-
fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef {
461-
const llvm_type = try self.getLLVMType(typed_value.ty, src);
505+
fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) !*const llvm.ValueRef {
506+
const llvm_type = try self.getLLVMType(tv.ty, src);
462507

463-
if (typed_value.val.isUndef())
508+
if (tv.val.isUndef())
464509
return llvm_type.getUndef();
465510

466-
switch (typed_value.ty.zigTypeTag()) {
467-
.Bool => return if (typed_value.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
468-
else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
511+
switch (tv.ty.zigTypeTag()) {
512+
.Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
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}),
469529
}
470530
}
471531

@@ -498,14 +558,14 @@ pub const LLVMIRModule = struct {
498558
);
499559
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) {
502562
llvm_fn.addFnAttr("noreturn");
503563
}
504564

505565
return llvm_fn;
506566
}
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 {
509569
switch (t.zigTypeTag()) {
510570
.Void => return llvm.voidType(),
511571
.NoReturn => return llvm.voidType(),
@@ -514,6 +574,11 @@ pub const LLVMIRModule = struct {
514574
return llvm.intType(info.bits);
515575
},
516576
.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+
},
517582
else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
518583
}
519584
}

src/llvm_bindings.zig

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,14 @@ pub const TypeRef = opaque {
4343
pub const constAllOnes = LLVMConstAllOnes;
4444
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+
4649
pub const getUndef = LLVMGetUndef;
4750
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;
4854
};
4955

5056
pub const ModuleRef = opaque {
@@ -82,6 +88,9 @@ pub const VerifierFailureAction = extern enum {
8288
ReturnStatus,
8389
};
8490

91+
pub const constNeg = LLVMConstNeg;
92+
extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef;
93+
8594
pub const voidType = LLVMVoidType;
8695
extern fn LLVMVoidType() *const TypeRef;
8796

@@ -143,6 +152,24 @@ pub const BuilderRef = opaque {
143152

144153
pub const buildNot = LLVMBuildNot;
145154
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;
146173
};
147174

148175
pub const BasicBlockRef = opaque {

0 commit comments

Comments
 (0)