authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-28 19:39:54-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-28 19:45:10-04:00
logaa92446365b97e167c4162122c66a02b4cbca031
tree1844fc64f41303478182e86cb7dca5aad956dab0
parente120b07a524f1accd4975f5206018c61c4be9345

stage2: implement function parameters

In codegen.zig, the std.Target.Cpu.Arch is now generally available as a comptime value where needed. This is a tradeoff that causes the compiler binary to be more bloated, but gives us higher performance, since the optimizer can optimize per architecture (which is usually how compilers are designed anyway, with different code per-architecture), and it also allows us to use per-architecture types, such as a Register enum that is specific to the comptime-known architecture. Adds abiSize method to Type.

3 files changed, 286 insertions(+), 111 deletions(-)

src-self-hosted/codegen.zig+190-107
...@@ -37,6 +37,69 @@ pub fn generateSymbol(...@@ -37,6 +37,69 @@ pub fn generateSymbol(
37 .Fn => {37 .Fn => {
38 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;38 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
3939
40 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
41 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
42 defer bin_file.allocator.free(param_types);
43 fn_type.fnParamTypes(param_types);
44 // A parameter may be broken into multiple machine code parameters, so we don't
45 // know the size up front.
46 var mc_args = try std.ArrayList(Function.MCValue).initCapacity(bin_file.allocator, param_types.len);
47 defer mc_args.deinit();
48
49 var next_stack_offset: u64 = 0;
50
51 switch (fn_type.fnCallingConvention()) {
52 .Naked => assert(mc_args.items.len == 0),
53 .Unspecified, .C => {
54 // Prepare the function parameters
55 switch (bin_file.options.target.cpu.arch) {
56 .x86_64 => {
57 const integer_registers = [_]Reg(.x86_64){.rdi, .rsi, .rdx, .rcx, .r8, .r9};
58 var next_int_reg: usize = 0;
59
60 for (param_types) |param_type, src_i| {
61 switch (param_type.zigTypeTag()) {
62 .Bool, .Int => {
63 if (next_int_reg >= integer_registers.len) {
64 try mc_args.append(.{ .stack_offset = next_stack_offset });
65 next_stack_offset += param_type.abiSize(bin_file.options.target);
66 } else {
67 try mc_args.append(.{ .register = @enumToInt(integer_registers[next_int_reg])});
68 next_int_reg += 1;
69 }
70 },
71 else => return Result{
72 .fail = try ErrorMsg.create(
73 bin_file.allocator,
74 src,
75 "TODO implement function parameters of type {}",
76 .{@tagName(param_type.zigTypeTag())},
77 ),
78 }
79 }
80 }
81
82 },
83 else => return Result{
84 .fail = try ErrorMsg.create(
85 bin_file.allocator,
86 src,
87 "TODO implement function parameters for {}",
88 .{bin_file.options.target.cpu.arch},
89 ),
90 },
91 }
92 },
93 else => return Result{
94 .fail = try ErrorMsg.create(
95 bin_file.allocator,
96 src,
97 "TODO implement {} calling convention",
98 .{fn_type.fnCallingConvention()},
99 ),
100 },
101 }
102
40 var function = Function{103 var function = Function{
41 .target = &bin_file.options.target,104 .target = &bin_file.options.target,
42 .bin_file = bin_file,105 .bin_file = bin_file,
...@@ -44,16 +107,14 @@ pub fn generateSymbol(...@@ -44,16 +107,14 @@ pub fn generateSymbol(
44 .code = code,107 .code = code,
45 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator),108 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator),
46 .err_msg = null,109 .err_msg = null,
110 .args = mc_args.items,
47 };111 };
48 defer function.inst_table.deinit();112 defer function.inst_table.deinit();
49113
50 for (module_fn.analysis.success.instructions) |inst| {114 function.gen() catch |err| switch (err) {
51 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {115 error.CodegenFail => return Result{ .fail = function.err_msg.? },
52 error.CodegenFail => return Result{ .fail = function.err_msg.? },116 else => |e| return e,
53 else => |e| return e,117 };
54 };
55 try function.inst_table.putNoClobber(inst, new_inst);
56 }
57118
58 if (function.err_msg) |em| {119 if (function.err_msg) |em| {
59 return Result{ .fail = em };120 return Result{ .fail = em };
...@@ -157,6 +218,7 @@ const Function = struct {...@@ -157,6 +218,7 @@ const Function = struct {
157 code: *std.ArrayList(u8),218 code: *std.ArrayList(u8),
158 inst_table: std.AutoHashMap(*ir.Inst, MCValue),219 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
159 err_msg: ?*ErrorMsg,220 err_msg: ?*ErrorMsg,
221 args: []MCValue,
160222
161 const MCValue = union(enum) {223 const MCValue = union(enum) {
162 none,224 none,
...@@ -170,44 +232,119 @@ const Function = struct {...@@ -170,44 +232,119 @@ const Function = struct {
170 register: usize,232 register: usize,
171 /// The value is in memory at a hard-coded address.233 /// The value is in memory at a hard-coded address.
172 memory: u64,234 memory: u64,
235 /// The value is one of the stack variables.
236 stack_offset: u64,
173 };237 };
174238
175 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {239 fn gen(self: *Function) !void {
240 switch (self.target.cpu.arch) {
241 .arm => return self.genArch(.arm),
242 .armeb => return self.genArch(.armeb),
243 .aarch64 => return self.genArch(.aarch64),
244 .aarch64_be => return self.genArch(.aarch64_be),
245 .aarch64_32 => return self.genArch(.aarch64_32),
246 .arc => return self.genArch(.arc),
247 .avr => return self.genArch(.avr),
248 .bpfel => return self.genArch(.bpfel),
249 .bpfeb => return self.genArch(.bpfeb),
250 .hexagon => return self.genArch(.hexagon),
251 .mips => return self.genArch(.mips),
252 .mipsel => return self.genArch(.mipsel),
253 .mips64 => return self.genArch(.mips64),
254 .mips64el => return self.genArch(.mips64el),
255 .msp430 => return self.genArch(.msp430),
256 .powerpc => return self.genArch(.powerpc),
257 .powerpc64 => return self.genArch(.powerpc64),
258 .powerpc64le => return self.genArch(.powerpc64le),
259 .r600 => return self.genArch(.r600),
260 .amdgcn => return self.genArch(.amdgcn),
261 .riscv32 => return self.genArch(.riscv32),
262 .riscv64 => return self.genArch(.riscv64),
263 .sparc => return self.genArch(.sparc),
264 .sparcv9 => return self.genArch(.sparcv9),
265 .sparcel => return self.genArch(.sparcel),
266 .s390x => return self.genArch(.s390x),
267 .tce => return self.genArch(.tce),
268 .tcele => return self.genArch(.tcele),
269 .thumb => return self.genArch(.thumb),
270 .thumbeb => return self.genArch(.thumbeb),
271 .i386 => return self.genArch(.i386),
272 .x86_64 => return self.genArch(.x86_64),
273 .xcore => return self.genArch(.xcore),
274 .nvptx => return self.genArch(.nvptx),
275 .nvptx64 => return self.genArch(.nvptx64),
276 .le32 => return self.genArch(.le32),
277 .le64 => return self.genArch(.le64),
278 .amdil => return self.genArch(.amdil),
279 .amdil64 => return self.genArch(.amdil64),
280 .hsail => return self.genArch(.hsail),
281 .hsail64 => return self.genArch(.hsail64),
282 .spir => return self.genArch(.spir),
283 .spir64 => return self.genArch(.spir64),
284 .kalimba => return self.genArch(.kalimba),
285 .shave => return self.genArch(.shave),
286 .lanai => return self.genArch(.lanai),
287 .wasm32 => return self.genArch(.wasm32),
288 .wasm64 => return self.genArch(.wasm64),
289 .renderscript32 => return self.genArch(.renderscript32),
290 .renderscript64 => return self.genArch(.renderscript64),
291 .ve => return self.genArch(.ve),
292 }
293 }
294
295 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {
296 for (self.mod_fn.analysis.success.instructions) |inst| {
297 const new_inst = try self.genFuncInst(inst, arch);
298 try self.inst_table.putNoClobber(inst, new_inst);
299 }
300 }
301
302 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {
176 switch (inst.tag) {303 switch (inst.tag) {
177 .add => return self.genAdd(inst.cast(ir.Inst.Add).?),304 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
178 .arg => return self.genArg(inst.src),305 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
179 .block => return self.genBlock(inst.cast(ir.Inst.Block).?),306 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
180 .breakpoint => return self.genBreakpoint(inst.src),307 .breakpoint => return self.genBreakpoint(inst.src, arch),
181 .call => return self.genCall(inst.cast(ir.Inst.Call).?),308 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),
182 .unreach => return MCValue{ .unreach = {} },309 .unreach => return MCValue{ .unreach = {} },
183 .constant => unreachable, // excluded from function bodies310 .constant => unreachable, // excluded from function bodies
184 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),311 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),
185 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),312 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
186 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),313 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
187 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),314 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
188 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),315 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
189 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),316 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
190 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),317 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
191 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),318 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
192 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),319 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
193 }320 }
194 }321 }
195322
196 fn genAdd(self: *Function, inst: *ir.Inst.Add) !MCValue {323 fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue {
197 switch (self.target.cpu.arch) {324 const lhs = try self.resolveInst(inst.args.lhs);
325 const rhs = try self.resolveInst(inst.args.rhs);
326 switch (arch) {
327 .i386, .x86_64 => {
328 // const lhs_reg = try self.instAsReg(lhs);
329 // const rhs_reg = try self.instAsReg(rhs);
330 // const result = try self.allocateReg();
331
332 // try self.code.append(??);
333
334 // lhs_reg.release();
335 // rhs_reg.release();
336 return self.fail(inst.base.src, "TODO implement register allocation", .{});
337 },
198 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),338 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
199 }339 }
200 }340 }
201341
202 fn genArg(self: *Function, src: usize) !MCValue {342 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {
203 switch (self.target.cpu.arch) {343 return self.args[inst.args.index];
204 else => return self.fail(src, "TODO implement function parameters for {}", .{self.target.cpu.arch}),
205 }
206 return .none;
207 }344 }
208345
209 fn genBreakpoint(self: *Function, src: usize) !MCValue {346 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {
210 switch (self.target.cpu.arch) {347 switch (arch) {
211 .i386, .x86_64 => {348 .i386, .x86_64 => {
212 try self.code.append(0xcc); // int3349 try self.code.append(0xcc); // int3
213 },350 },
...@@ -216,8 +353,8 @@ const Function = struct {...@@ -216,8 +353,8 @@ const Function = struct {
216 return .none;353 return .none;
217 }354 }
218355
219 fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue {356 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {
220 switch (self.target.cpu.arch) {357 switch (arch) {
221 .x86_64, .i386 => {358 .x86_64, .i386 => {
222 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {359 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
223 if (inst.args.args.len != 0) {360 if (inst.args.args.len != 0) {
...@@ -251,11 +388,11 @@ const Function = struct {...@@ -251,11 +388,11 @@ const Function = struct {
251 }388 }
252 }389 }
253390
254 fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue {391 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {
255 if (mcv != .none) {392 if (mcv != .none) {
256 return self.fail(src, "TODO implement return with non-void operand", .{});393 return self.fail(src, "TODO implement return with non-void operand", .{});
257 }394 }
258 switch (self.target.cpu.arch) {395 switch (arch) {
259 .i386, .x86_64 => {396 .i386, .x86_64 => {
260 try self.code.append(0xc3); // ret397 try self.code.append(0xc3); // ret
261 },398 },
...@@ -264,43 +401,43 @@ const Function = struct {...@@ -264,43 +401,43 @@ const Function = struct {
264 return .unreach;401 return .unreach;
265 }402 }
266403
267 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {404 fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue {
268 const operand = try self.resolveInst(inst.args.operand);405 const operand = try self.resolveInst(inst.args.operand);
269 return self.ret(inst.base.src, operand);406 return self.ret(inst.base.src, arch, operand);
270 }407 }
271408
272 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue {409 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
273 return self.ret(inst.base.src, .none);410 return self.ret(inst.base.src, arch, .none);
274 }411 }
275412
276 fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {413 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {
277 switch (self.target.cpu.arch) {414 switch (arch) {
278 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),415 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
279 }416 }
280 }417 }
281418
282 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {419 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
283 switch (self.target.cpu.arch) {420 switch (arch) {
284 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),421 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
285 }422 }
286 }423 }
287424
288 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {425 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
289 switch (self.target.cpu.arch) {426 switch (arch) {
290 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),427 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
291 }428 }
292 }429 }
293430
294 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {431 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
295 // Here you can specialize this instruction if it makes sense to, otherwise the default432 // Here you can specialize this instruction if it makes sense to, otherwise the default
296 // will call genIsNull and invert the result.433 // will call genIsNull and invert the result.
297 switch (self.target.cpu.arch) {434 switch (arch) {
298 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),435 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
299 }436 }
300 }437 }
301438
302 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {439 fn genRelativeFwdJump(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, amount: u32) !void {
303 switch (self.target.cpu.arch) {440 switch (arch) {
304 .i386, .x86_64 => {441 .i386, .x86_64 => {
305 // TODO x86 treats the operands as signed442 // TODO x86 treats the operands as signed
306 if (amount <= std.math.maxInt(u8)) {443 if (amount <= std.math.maxInt(u8)) {
...@@ -318,70 +455,13 @@ const Function = struct {...@@ -318,70 +455,13 @@ const Function = struct {
318 }455 }
319 }456 }
320457
321 fn genBlock(self: *Function, inst: *ir.Inst.Block) !MCValue {458 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
322 switch (self.target.cpu.arch) {459 switch (arch) {
323 else => return self.fail(inst.base.src, "TODO implement codegen Block for {}", .{self.target.cpu.arch}),460 else => return self.fail(inst.base.src, "TODO implement codegen Block for {}", .{self.target.cpu.arch}),
324 }461 }
325 }462 }
326463
327 fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {464 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {
328 // TODO convert to inline function
329 switch (self.target.cpu.arch) {
330 .arm => return self.genAsmArch(.arm, inst),
331 .armeb => return self.genAsmArch(.armeb, inst),
332 .aarch64 => return self.genAsmArch(.aarch64, inst),
333 .aarch64_be => return self.genAsmArch(.aarch64_be, inst),
334 .aarch64_32 => return self.genAsmArch(.aarch64_32, inst),
335 .arc => return self.genAsmArch(.arc, inst),
336 .avr => return self.genAsmArch(.avr, inst),
337 .bpfel => return self.genAsmArch(.bpfel, inst),
338 .bpfeb => return self.genAsmArch(.bpfeb, inst),
339 .hexagon => return self.genAsmArch(.hexagon, inst),
340 .mips => return self.genAsmArch(.mips, inst),
341 .mipsel => return self.genAsmArch(.mipsel, inst),
342 .mips64 => return self.genAsmArch(.mips64, inst),
343 .mips64el => return self.genAsmArch(.mips64el, inst),
344 .msp430 => return self.genAsmArch(.msp430, inst),
345 .powerpc => return self.genAsmArch(.powerpc, inst),
346 .powerpc64 => return self.genAsmArch(.powerpc64, inst),
347 .powerpc64le => return self.genAsmArch(.powerpc64le, inst),
348 .r600 => return self.genAsmArch(.r600, inst),
349 .amdgcn => return self.genAsmArch(.amdgcn, inst),
350 .riscv32 => return self.genAsmArch(.riscv32, inst),
351 .riscv64 => return self.genAsmArch(.riscv64, inst),
352 .sparc => return self.genAsmArch(.sparc, inst),
353 .sparcv9 => return self.genAsmArch(.sparcv9, inst),
354 .sparcel => return self.genAsmArch(.sparcel, inst),
355 .s390x => return self.genAsmArch(.s390x, inst),
356 .tce => return self.genAsmArch(.tce, inst),
357 .tcele => return self.genAsmArch(.tcele, inst),
358 .thumb => return self.genAsmArch(.thumb, inst),
359 .thumbeb => return self.genAsmArch(.thumbeb, inst),
360 .i386 => return self.genAsmArch(.i386, inst),
361 .x86_64 => return self.genAsmArch(.x86_64, inst),
362 .xcore => return self.genAsmArch(.xcore, inst),
363 .nvptx => return self.genAsmArch(.nvptx, inst),
364 .nvptx64 => return self.genAsmArch(.nvptx64, inst),
365 .le32 => return self.genAsmArch(.le32, inst),
366 .le64 => return self.genAsmArch(.le64, inst),
367 .amdil => return self.genAsmArch(.amdil, inst),
368 .amdil64 => return self.genAsmArch(.amdil64, inst),
369 .hsail => return self.genAsmArch(.hsail, inst),
370 .hsail64 => return self.genAsmArch(.hsail64, inst),
371 .spir => return self.genAsmArch(.spir, inst),
372 .spir64 => return self.genAsmArch(.spir64, inst),
373 .kalimba => return self.genAsmArch(.kalimba, inst),
374 .shave => return self.genAsmArch(.shave, inst),
375 .lanai => return self.genAsmArch(.lanai, inst),
376 .wasm32 => return self.genAsmArch(.wasm32, inst),
377 .wasm64 => return self.genAsmArch(.wasm64, inst),
378 .renderscript32 => return self.genAsmArch(.renderscript32, inst),
379 .renderscript64 => return self.genAsmArch(.renderscript64, inst),
380 .ve => return self.genAsmArch(.ve, inst),
381 }
382 }
383
384 fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue {
385 if (arch != .x86_64 and arch != .i386) {465 if (arch != .x86_64 and arch != .i386) {
386 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});466 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
387 }467 }
...@@ -607,6 +687,9 @@ const Function = struct {...@@ -607,6 +687,9 @@ const Function = struct {
607 }687 }
608 }688 }
609 },689 },
690 .stack_offset => |off| {
691 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
692 }
610 },693 },
611 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),694 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
612 }695 }
src-self-hosted/codegen/x86_64.zig+26-4
...@@ -1,20 +1,21 @@...@@ -1,20 +1,21 @@
1const Type = @import("../Type.zig");
2
1// zig fmt: off3// zig fmt: off
24
3/// Definitions of all of the x64 registers. The order is very, very important.5/// Definitions of all of the x64 registers. The order is semantically meaningful.
4/// The registers are defined such that IDs go in descending order of 64-bit,6/// The registers are defined such that IDs go in descending order of 64-bit,
5/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen7/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
6/// registers. This results in some very, very useful properties:8/// registers. This results in some useful properties:
7///9///
8/// Any 64-bit register can be turned into its 32-bit form by adding 16, and10/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
9/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it11/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
10/// works for all except for sp, bp, si, and di, which don't *have* an 8-bit12/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
11/// form.13/// form.
12///14///
13/// If (register & 8) is set, the register is extended.15/// If (register & 8) is set, the register is extended.
14///16///
15/// The ID can be easily determined by figuring out what range the register is17/// The ID can be easily determined by figuring out what range the register is
16/// in, and then subtracting the base.18/// in, and then subtracting the base.
17///
18pub const Register = enum(u8) {19pub const Register = enum(u8) {
19 // 0 through 15, 64-bit registers. 8-15 are extended.20 // 0 through 15, 64-bit registers. 8-15 are extended.
20 // id is just the int value.21 // id is just the int value.
...@@ -67,3 +68,24 @@ pub const Register = enum(u8) {...@@ -67,3 +68,24 @@ pub const Register = enum(u8) {
67};68};
6869
69// zig fmt: on70// zig fmt: on
71
72/// After argument values have been computed, they are placed either in registers
73/// or pushed on the stack. The way values are passed depends on the class.
74pub const ParameterClass = enum {
75 /// Integral types that fit into one of the general purpose registers.
76 integer,
77 /// Types that fit into a vector register.
78 sse,
79 /// Types that fit into a vector register and can be passed
80 /// and returned in the upper bytes of it.
81 sse_up,
82 /// Types that will be returned via the x87FPU.
83 x87,
84 /// Types that will be returned via the x87FPU and can be passed and returned
85 /// in the upper bytes of it.
86 x87_up,
87 /// Types that will be returned via the x87FPU.
88 complex_x87,
89 /// Types that will be passed and returned in mem-ory via the stack.
90 memory,
91};
src-self-hosted/type.zig+70
...@@ -535,6 +535,76 @@ pub const Type = extern union {...@@ -535,6 +535,76 @@ pub const Type = extern union {
535 };535 };
536 }536 }
537537
538 /// Asserts the type has the ABI size already resolved.
539 pub fn abiSize(self: Type, target: Target) u64 {
540 return switch (self.tag()) {
541 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
542 .fn_void_no_args => unreachable, // represents machine code; not a pointer
543 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
544 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
545 .function => unreachable, // represents machine code; not a pointer
546 .c_void => unreachable,
547 .void => unreachable,
548 .type => unreachable,
549 .comptime_int => unreachable,
550 .comptime_float => unreachable,
551 .noreturn => unreachable,
552 .@"null" => unreachable,
553 .@"undefined" => unreachable,
554
555 .u8,
556 .i8,
557 .bool,
558 => return 1,
559
560 .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
561 .array => {
562 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
563 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
564 return payload.len * elem_size;
565 },
566 .i16, .u16 => return 2,
567 .i32, .u32 => return 4,
568 .i64, .u64 => return 8,
569
570 .isize,
571 .usize,
572 .single_const_pointer_to_comptime_int,
573 .const_slice_u8,
574 .single_const_pointer,
575 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
576
577 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
578 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
579 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
580 .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
581 .c_long => return @divExact(CType.long.sizeInBits(target), 8),
582 .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
583 .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
584 .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
585
586 .f16 => return 2,
587 .f32 => return 4,
588 .f64 => return 8,
589 .f128 => return 16,
590 .c_longdouble => return 16,
591
592 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
593
594
595 .int_signed, .int_unsigned => {
596 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
597 pl.bits
598 else if (self.cast(Payload.IntUnsigned)) |pl|
599 pl.bits
600 else
601 unreachable;
602
603 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
604 },
605 };
606 }
607
538 pub fn isSinglePointer(self: Type) bool {608 pub fn isSinglePointer(self: Type) bool {
539 return switch (self.tag()) {609 return switch (self.tag()) {
540 .u8,610 .u8,