Skip to content

Commit 245d98d

Browse files
authored
Merge pull request #6291 from pixelherodev/cbe_arithmetic
CBE: addition and subtraction
2 parents 9241c1b + 7d69e1d commit 245d98d

File tree

3 files changed

+176
-38
lines changed

3 files changed

+176
-38
lines changed

src/Module.zig

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,43 +2638,52 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
26382638
if (instructions.len == 1)
26392639
return instructions[0].ty;
26402640

2641-
var prev_inst = instructions[0];
2642-
for (instructions[1..]) |next_inst| {
2643-
if (next_inst.ty.eql(prev_inst.ty))
2641+
var chosen = instructions[0];
2642+
for (instructions[1..]) |candidate| {
2643+
if (candidate.ty.eql(chosen.ty))
26442644
continue;
2645-
if (next_inst.ty.zigTypeTag() == .NoReturn)
2645+
if (candidate.ty.zigTypeTag() == .NoReturn)
26462646
continue;
2647-
if (prev_inst.ty.zigTypeTag() == .NoReturn) {
2648-
prev_inst = next_inst;
2647+
if (chosen.ty.zigTypeTag() == .NoReturn) {
2648+
chosen = candidate;
26492649
continue;
26502650
}
2651-
if (next_inst.ty.zigTypeTag() == .Undefined)
2651+
if (candidate.ty.zigTypeTag() == .Undefined)
26522652
continue;
2653-
if (prev_inst.ty.zigTypeTag() == .Undefined) {
2654-
prev_inst = next_inst;
2653+
if (chosen.ty.zigTypeTag() == .Undefined) {
2654+
chosen = candidate;
26552655
continue;
26562656
}
2657-
if (prev_inst.ty.isInt() and
2658-
next_inst.ty.isInt() and
2659-
prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
2657+
if (chosen.ty.isInt() and
2658+
candidate.ty.isInt() and
2659+
chosen.ty.isSignedInt() == candidate.ty.isSignedInt())
26602660
{
2661-
if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {
2662-
prev_inst = next_inst;
2661+
if (chosen.ty.intInfo(self.getTarget()).bits < candidate.ty.intInfo(self.getTarget()).bits) {
2662+
chosen = candidate;
26632663
}
26642664
continue;
26652665
}
2666-
if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
2667-
if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
2668-
prev_inst = next_inst;
2666+
if (chosen.ty.isFloat() and candidate.ty.isFloat()) {
2667+
if (chosen.ty.floatBits(self.getTarget()) < candidate.ty.floatBits(self.getTarget())) {
2668+
chosen = candidate;
26692669
}
26702670
continue;
26712671
}
26722672

2673+
if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) {
2674+
chosen = candidate;
2675+
continue;
2676+
}
2677+
2678+
if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) {
2679+
continue;
2680+
}
2681+
26732682
// TODO error notes pointing out each type
2674-
return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
2683+
return self.fail(scope, candidate.src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty });
26752684
}
26762685

2677-
return prev_inst.ty;
2686+
return chosen.ty;
26782687
}
26792688

26802689
pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {

src/codegen/c.zig

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const C = link.File.C;
1111
const Decl = Module.Decl;
1212
const mem = std.mem;
1313

14+
const indentation = " ";
15+
1416
/// Maps a name from Zig source to C. Currently, this will always give the same
1517
/// output for any given input, sometimes resulting in broken identifiers.
1618
fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
@@ -52,8 +54,10 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va
5254
fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
5355
const tv = decl.typed_value.most_recent.typed_value;
5456
try renderType(ctx, writer, tv.ty.fnReturnType());
55-
const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name));
56-
defer ctx.file.base.allocator.free(name);
57+
// Use the child allocator directly, as we know the name can be freed before
58+
// the rest of the arena.
59+
const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name));
60+
defer ctx.arena.child_allocator.free(name);
5761
try writer.print(" {}(", .{name});
5862
var param_len = tv.ty.fnParamLen();
5963
if (param_len == 0)
@@ -87,6 +91,7 @@ fn genArray(file: *C, decl: *Decl) !void {
8791
if (tv.val.cast(Value.Payload.Bytes)) |payload|
8892
if (tv.ty.sentinel()) |sentinel|
8993
if (sentinel.toUnsignedInt() == 0)
94+
// TODO: static by default
9095
try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
9196
else
9297
return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
@@ -99,22 +104,30 @@ fn genArray(file: *C, decl: *Decl) !void {
99104
const Context = struct {
100105
file: *C,
101106
decl: *Decl,
102-
inst_map: std.AutoHashMap(*Inst, []u8),
107+
inst_map: *std.AutoHashMap(*Inst, []u8),
108+
arena: *std.heap.ArenaAllocator,
103109
argdex: usize = 0,
104110
unnamed_index: usize = 0,
105111

112+
fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
113+
if (inst.cast(Inst.Constant)) |const_inst| {
114+
var out = std.ArrayList(u8).init(&self.arena.allocator);
115+
try renderValue(self, out.writer(), inst.ty, const_inst.val);
116+
return out.toOwnedSlice();
117+
}
118+
if (self.inst_map.get(inst)) |val| {
119+
return val;
120+
}
121+
unreachable;
122+
}
123+
106124
fn name(self: *Context) ![]u8 {
107-
const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index});
125+
const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{}", .{self.unnamed_index});
108126
self.unnamed_index += 1;
109127
return val;
110128
}
111129

112130
fn deinit(self: *Context) void {
113-
var it = self.inst_map.iterator();
114-
while (it.next()) |kv| {
115-
self.file.base.allocator.free(kv.value);
116-
}
117-
self.inst_map.deinit();
118131
self.* = undefined;
119132
}
120133
};
@@ -123,10 +136,15 @@ fn genFn(file: *C, decl: *Decl) !void {
123136
const writer = file.main.writer();
124137
const tv = decl.typed_value.most_recent.typed_value;
125138

139+
var arena = std.heap.ArenaAllocator.init(file.base.allocator);
140+
defer arena.deinit();
141+
var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
142+
defer inst_map.deinit();
126143
var ctx = Context{
127144
.file = file,
128145
.decl = decl,
129-
.inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator),
146+
.arena = &arena,
147+
.inst_map = &inst_map,
130148
};
131149
defer ctx.deinit();
132150

@@ -142,6 +160,8 @@ fn genFn(file: *C, decl: *Decl) !void {
142160
if (switch (inst.tag) {
143161
.assembly => try genAsm(&ctx, inst.castTag(.assembly).?),
144162
.call => try genCall(&ctx, inst.castTag(.call).?),
163+
.add => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "+"),
164+
.sub => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "-"),
145165
.ret => try genRet(&ctx, inst.castTag(.ret).?),
146166
.retvoid => try genRetVoid(&ctx),
147167
.arg => try genArg(&ctx),
@@ -160,13 +180,13 @@ fn genFn(file: *C, decl: *Decl) !void {
160180
}
161181

162182
fn genArg(ctx: *Context) !?[]u8 {
163-
const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex});
183+
const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});
164184
ctx.argdex += 1;
165185
return name;
166186
}
167187

168188
fn genRetVoid(ctx: *Context) !?[]u8 {
169-
try ctx.file.main.writer().print(" return;\n", .{});
189+
try ctx.file.main.writer().print(indentation ++ "return;\n", .{});
170190
return null;
171191
}
172192

@@ -180,20 +200,32 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
180200
const op = inst.operand;
181201
const writer = ctx.file.main.writer();
182202
const name = try ctx.name();
183-
const from = ctx.inst_map.get(op) orelse
184-
return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{});
185-
try writer.writeAll(" const ");
203+
const from = try ctx.resolveInst(inst.operand);
204+
try writer.writeAll(indentation ++ "const ");
186205
try renderType(ctx, writer, inst.base.ty);
187206
try writer.print(" {} = (", .{name});
188207
try renderType(ctx, writer, inst.base.ty);
189208
try writer.print("){};\n", .{from});
190209
return name;
191210
}
192211

212+
fn genBinOp(ctx: *Context, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 {
213+
if (inst.base.isUnused())
214+
return null;
215+
const lhs = ctx.resolveInst(inst.lhs);
216+
const rhs = ctx.resolveInst(inst.rhs);
217+
const writer = ctx.file.main.writer();
218+
const name = try ctx.name();
219+
try writer.writeAll(indentation ++ "const ");
220+
try renderType(ctx, writer, inst.base.ty);
221+
try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });
222+
return name;
223+
}
224+
193225
fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
194226
const writer = ctx.file.main.writer();
195227
const header = ctx.file.header.writer();
196-
try writer.writeAll(" ");
228+
try writer.writeAll(indentation);
197229
if (inst.func.castTag(.constant)) |func_inst| {
198230
if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
199231
const target = func_val.func.owner_decl;
@@ -217,7 +249,8 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
217249
if (arg.cast(Inst.Constant)) |con| {
218250
try renderValue(ctx, writer, arg.ty, con.val);
219251
} else {
220-
return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg});
252+
const val = try ctx.resolveInst(arg);
253+
try writer.print("{}", .{val});
221254
}
222255
}
223256
}
@@ -242,13 +275,13 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
242275
}
243276

244277
fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
245-
try ctx.file.main.writer().writeAll(" zig_unreachable();\n");
278+
try ctx.file.main.writer().writeAll(indentation ++ "zig_unreachable();\n");
246279
return null;
247280
}
248281

249282
fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {
250283
const writer = ctx.file.main.writer();
251-
try writer.writeAll(" ");
284+
try writer.writeAll(indentation);
252285
for (as.inputs) |i, index| {
253286
if (i[0] == '{' and i[i.len - 1] == '}') {
254287
const reg = i[1 .. i.len - 1];

test/stage2/cbe.zig

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,100 @@ pub fn addCases(ctx: *TestContext) !void {
147147
\\}
148148
\\
149149
);
150+
ctx.c("exit with u8 arithmetic", linux_x64,
151+
\\export fn _start() noreturn {
152+
\\ exitMath(1);
153+
\\}
154+
\\
155+
\\fn exitMath(a: u8) noreturn {
156+
\\ exit(0 + a - a);
157+
\\}
158+
\\
159+
\\fn exit(code: u8) noreturn {
160+
\\ asm volatile ("syscall"
161+
\\ :
162+
\\ : [number] "{rax}" (231),
163+
\\ [arg1] "{rdi}" (code)
164+
\\ );
165+
\\ unreachable;
166+
\\}
167+
\\
168+
,
169+
\\#include <stddef.h>
170+
\\#include <stdint.h>
171+
\\
172+
\\zig_noreturn void exitMath(uint8_t arg0);
173+
\\zig_noreturn void exit(uint8_t arg0);
174+
\\
175+
\\const char *const exit__anon_0 = "{rax}";
176+
\\const char *const exit__anon_1 = "{rdi}";
177+
\\const char *const exit__anon_2 = "syscall";
178+
\\
179+
\\zig_noreturn void _start(void) {
180+
\\ exitMath(1);
181+
\\}
182+
\\
183+
\\zig_noreturn void exitMath(uint8_t arg0) {
184+
\\ const uint8_t __temp_0 = 0 + arg0;
185+
\\ const uint8_t __temp_1 = __temp_0 - arg0;
186+
\\ exit(__temp_1);
187+
\\}
188+
\\
189+
\\zig_noreturn void exit(uint8_t arg0) {
190+
\\ const size_t __temp_0 = (size_t)arg0;
191+
\\ register size_t rax_constant __asm__("rax") = 231;
192+
\\ register size_t rdi_constant __asm__("rdi") = __temp_0;
193+
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
194+
\\ zig_unreachable();
195+
\\}
196+
\\
197+
);
198+
ctx.c("exit with u8 arithmetic inverted", linux_x64,
199+
\\export fn _start() noreturn {
200+
\\ exitMath(1);
201+
\\}
202+
\\
203+
\\fn exitMath(a: u8) noreturn {
204+
\\ exit(a + 0 - a);
205+
\\}
206+
\\
207+
\\fn exit(code: u8) noreturn {
208+
\\ asm volatile ("syscall"
209+
\\ :
210+
\\ : [number] "{rax}" (231),
211+
\\ [arg1] "{rdi}" (code)
212+
\\ );
213+
\\ unreachable;
214+
\\}
215+
\\
216+
,
217+
\\#include <stddef.h>
218+
\\#include <stdint.h>
219+
\\
220+
\\zig_noreturn void exitMath(uint8_t arg0);
221+
\\zig_noreturn void exit(uint8_t arg0);
222+
\\
223+
\\const char *const exit__anon_0 = "{rax}";
224+
\\const char *const exit__anon_1 = "{rdi}";
225+
\\const char *const exit__anon_2 = "syscall";
226+
\\
227+
\\zig_noreturn void _start(void) {
228+
\\ exitMath(1);
229+
\\}
230+
\\
231+
\\zig_noreturn void exitMath(uint8_t arg0) {
232+
\\ const uint8_t __temp_0 = arg0 + 0;
233+
\\ const uint8_t __temp_1 = __temp_0 - arg0;
234+
\\ exit(__temp_1);
235+
\\}
236+
\\
237+
\\zig_noreturn void exit(uint8_t arg0) {
238+
\\ const size_t __temp_0 = (size_t)arg0;
239+
\\ register size_t rax_constant __asm__("rax") = 231;
240+
\\ register size_t rdi_constant __asm__("rdi") = __temp_0;
241+
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
242+
\\ zig_unreachable();
243+
\\}
244+
\\
245+
);
150246
}

0 commit comments

Comments
 (0)