authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-17 01:00:38-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-17 01:00:38-04:00
log245d98d32dd29e80de9732f415a4731748008acf
tree9caf68c3f47a8804bdacbc6a3943d2879de9de3d
parent9241c1b7728aa2e24a9db34d7f4d4ab031b4b792
parent7d69e1d84e272251e6aead39c551154797c2b1a4
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6291 from pixelherodev/cbe_arithmetic

CBE: addition and subtraction

3 files changed, 176 insertions(+), 38 deletions(-)

src/Module.zig+28-19
...@@ -2638,43 +2638,52 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty...@@ -2638,43 +2638,52 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
2638 if (instructions.len == 1)2638 if (instructions.len == 1)
2639 return instructions[0].ty;2639 return instructions[0].ty;
26402640
2641 var prev_inst = instructions[0];2641 var chosen = instructions[0];
2642 for (instructions[1..]) |next_inst| {2642 for (instructions[1..]) |candidate| {
2643 if (next_inst.ty.eql(prev_inst.ty))2643 if (candidate.ty.eql(chosen.ty))
2644 continue;2644 continue;
2645 if (next_inst.ty.zigTypeTag() == .NoReturn)2645 if (candidate.ty.zigTypeTag() == .NoReturn)
2646 continue;2646 continue;
2647 if (prev_inst.ty.zigTypeTag() == .NoReturn) {2647 if (chosen.ty.zigTypeTag() == .NoReturn) {
2648 prev_inst = next_inst;2648 chosen = candidate;
2649 continue;2649 continue;
2650 }2650 }
2651 if (next_inst.ty.zigTypeTag() == .Undefined)2651 if (candidate.ty.zigTypeTag() == .Undefined)
2652 continue;2652 continue;
2653 if (prev_inst.ty.zigTypeTag() == .Undefined) {2653 if (chosen.ty.zigTypeTag() == .Undefined) {
2654 prev_inst = next_inst;2654 chosen = candidate;
2655 continue;2655 continue;
2656 }2656 }
2657 if (prev_inst.ty.isInt() and2657 if (chosen.ty.isInt() and
2658 next_inst.ty.isInt() and2658 candidate.ty.isInt() and
2659 prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())2659 chosen.ty.isSignedInt() == candidate.ty.isSignedInt())
2660 {2660 {
2661 if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {2661 if (chosen.ty.intInfo(self.getTarget()).bits < candidate.ty.intInfo(self.getTarget()).bits) {
2662 prev_inst = next_inst;2662 chosen = candidate;
2663 }2663 }
2664 continue;2664 continue;
2665 }2665 }
2666 if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {2666 if (chosen.ty.isFloat() and candidate.ty.isFloat()) {
2667 if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {2667 if (chosen.ty.floatBits(self.getTarget()) < candidate.ty.floatBits(self.getTarget())) {
2668 prev_inst = next_inst;2668 chosen = candidate;
2669 }2669 }
2670 continue;2670 continue;
2671 }2671 }
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
2673 // TODO error notes pointing out each type2682 // 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 });
2675 }2684 }
26762685
2677 return prev_inst.ty;2686 return chosen.ty;
2678}2687}
26792688
2680pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {2689pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
src/codegen/c.zig+52-19
...@@ -11,6 +11,8 @@ const C = link.File.C;...@@ -11,6 +11,8 @@ const C = link.File.C;
11const Decl = Module.Decl;11const Decl = Module.Decl;
12const mem = std.mem;12const mem = std.mem;
1313
14const indentation = " ";
15
14/// Maps a name from Zig source to C. Currently, this will always give the same16/// Maps a name from Zig source to C. Currently, this will always give the same
15/// output for any given input, sometimes resulting in broken identifiers.17/// output for any given input, sometimes resulting in broken identifiers.
16fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {18fn 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...@@ -52,8 +54,10 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va
52fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {54fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
53 const tv = decl.typed_value.most_recent.typed_value;55 const tv = decl.typed_value.most_recent.typed_value;
54 try renderType(ctx, writer, tv.ty.fnReturnType());56 try renderType(ctx, writer, tv.ty.fnReturnType());
55 const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name));57 // Use the child allocator directly, as we know the name can be freed before
56 defer ctx.file.base.allocator.free(name);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);
57 try writer.print(" {}(", .{name});61 try writer.print(" {}(", .{name});
58 var param_len = tv.ty.fnParamLen();62 var param_len = tv.ty.fnParamLen();
59 if (param_len == 0)63 if (param_len == 0)
...@@ -87,6 +91,7 @@ fn genArray(file: *C, decl: *Decl) !void {...@@ -87,6 +91,7 @@ fn genArray(file: *C, decl: *Decl) !void {
87 if (tv.val.cast(Value.Payload.Bytes)) |payload|91 if (tv.val.cast(Value.Payload.Bytes)) |payload|
88 if (tv.ty.sentinel()) |sentinel|92 if (tv.ty.sentinel()) |sentinel|
89 if (sentinel.toUnsignedInt() == 0)93 if (sentinel.toUnsignedInt() == 0)
94 // TODO: static by default
90 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })95 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
91 else96 else
92 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})97 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
...@@ -99,22 +104,30 @@ fn genArray(file: *C, decl: *Decl) !void {...@@ -99,22 +104,30 @@ fn genArray(file: *C, decl: *Decl) !void {
99const Context = struct {104const Context = struct {
100 file: *C,105 file: *C,
101 decl: *Decl,106 decl: *Decl,
102 inst_map: std.AutoHashMap(*Inst, []u8),107 inst_map: *std.AutoHashMap(*Inst, []u8),
108 arena: *std.heap.ArenaAllocator,
103 argdex: usize = 0,109 argdex: usize = 0,
104 unnamed_index: usize = 0,110 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
106 fn name(self: *Context) ![]u8 {124 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});
108 self.unnamed_index += 1;126 self.unnamed_index += 1;
109 return val;127 return val;
110 }128 }
111129
112 fn deinit(self: *Context) void {130 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();
118 self.* = undefined;131 self.* = undefined;
119 }132 }
120};133};
...@@ -123,10 +136,15 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -123,10 +136,15 @@ fn genFn(file: *C, decl: *Decl) !void {
123 const writer = file.main.writer();136 const writer = file.main.writer();
124 const tv = decl.typed_value.most_recent.typed_value;137 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();
126 var ctx = Context{143 var ctx = Context{
127 .file = file,144 .file = file,
128 .decl = decl,145 .decl = decl,
129 .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator),146 .arena = &arena,
147 .inst_map = &inst_map,
130 };148 };
131 defer ctx.deinit();149 defer ctx.deinit();
132150
...@@ -142,6 +160,8 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -142,6 +160,8 @@ fn genFn(file: *C, decl: *Decl) !void {
142 if (switch (inst.tag) {160 if (switch (inst.tag) {
143 .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),161 .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),
144 .call => try genCall(&ctx, inst.castTag(.call).?),162 .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).?, "-"),
145 .ret => try genRet(&ctx, inst.castTag(.ret).?),165 .ret => try genRet(&ctx, inst.castTag(.ret).?),
146 .retvoid => try genRetVoid(&ctx),166 .retvoid => try genRetVoid(&ctx),
147 .arg => try genArg(&ctx),167 .arg => try genArg(&ctx),
...@@ -160,13 +180,13 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -160,13 +180,13 @@ fn genFn(file: *C, decl: *Decl) !void {
160}180}
161181
162fn genArg(ctx: *Context) !?[]u8 {182fn 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});
164 ctx.argdex += 1;184 ctx.argdex += 1;
165 return name;185 return name;
166}186}
167187
168fn genRetVoid(ctx: *Context) !?[]u8 {188fn genRetVoid(ctx: *Context) !?[]u8 {
169 try ctx.file.main.writer().print(" return;\n", .{});189 try ctx.file.main.writer().print(indentation ++ "return;\n", .{});
170 return null;190 return null;
171}191}
172192
...@@ -180,9 +200,8 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {...@@ -180,9 +200,8 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
180 const op = inst.operand;200 const op = inst.operand;
181 const writer = ctx.file.main.writer();201 const writer = ctx.file.main.writer();
182 const name = try ctx.name();202 const name = try ctx.name();
183 const from = ctx.inst_map.get(op) orelse203 const from = try ctx.resolveInst(inst.operand);
184 return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{});204 try writer.writeAll(indentation ++ "const ");
185 try writer.writeAll(" const ");
186 try renderType(ctx, writer, inst.base.ty);205 try renderType(ctx, writer, inst.base.ty);
187 try writer.print(" {} = (", .{name});206 try writer.print(" {} = (", .{name});
188 try renderType(ctx, writer, inst.base.ty);207 try renderType(ctx, writer, inst.base.ty);
...@@ -190,10 +209,23 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {...@@ -190,10 +209,23 @@ fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
190 return name;209 return name;
191}210}
192211
212fn 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
193fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {225fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
194 const writer = ctx.file.main.writer();226 const writer = ctx.file.main.writer();
195 const header = ctx.file.header.writer();227 const header = ctx.file.header.writer();
196 try writer.writeAll(" ");228 try writer.writeAll(indentation);
197 if (inst.func.castTag(.constant)) |func_inst| {229 if (inst.func.castTag(.constant)) |func_inst| {
198 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {230 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
199 const target = func_val.func.owner_decl;231 const target = func_val.func.owner_decl;
...@@ -217,7 +249,8 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {...@@ -217,7 +249,8 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
217 if (arg.cast(Inst.Constant)) |con| {249 if (arg.cast(Inst.Constant)) |con| {
218 try renderValue(ctx, writer, arg.ty, con.val);250 try renderValue(ctx, writer, arg.ty, con.val);
219 } else {251 } 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});
221 }254 }
222 }255 }
223 }256 }
...@@ -242,13 +275,13 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {...@@ -242,13 +275,13 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
242}275}
243276
244fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {277fn 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");
246 return null;279 return null;
247}280}
248281
249fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {282fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {
250 const writer = ctx.file.main.writer();283 const writer = ctx.file.main.writer();
251 try writer.writeAll(" ");284 try writer.writeAll(indentation);
252 for (as.inputs) |i, index| {285 for (as.inputs) |i, index| {
253 if (i[0] == '{' and i[i.len - 1] == '}') {286 if (i[0] == '{' and i[i.len - 1] == '}') {
254 const reg = i[1 .. i.len - 1];287 const reg = i[1 .. i.len - 1];
test/stage2/cbe.zig+96
...@@ -147,4 +147,100 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -147,4 +147,100 @@ pub fn addCases(ctx: *TestContext) !void {
147 \\}147 \\}
148 \\148 \\
149 );149 );
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 );
150}246}