authorgravatar for 81774659+gracefuu@users.noreply.github.comgracefu <81774659+gracefuu@users.noreply.github.com> 2021-04-08 17:05:48+08:00
committergravatar for 81774659+gracefuu@users.noreply.github.comgracefu <81774659+gracefuu@users.noreply.github.com> 2021-04-16 15:21:16+08:00
loge1959ccd4e74612d792f098dfbe7c0ae31813653
tree7bd9cf2aebe755cc430b9a6e3fb402bfb6c53a23
parentd4d21dd46d69961300cc796abdebe352dfe6eae8
signature Commit is signed but in an unrecognized format.

stage2 x86_64: add instruction encoder helper fn


1 files changed, 421 insertions(+), 0 deletions(-)

src/codegen/x86_64.zig+421
......@@ -1,4 +1,8 @@
11const std = @import("std");
2const testing = std.testing;
3const mem = std.mem;
4const assert = std.debug.assert;
5const ArrayList = std.ArrayList;
26const Type = @import("../Type.zig");
37const DW = std.dwarf;
48
......@@ -68,6 +72,11 @@ pub const Register = enum(u8) {
6872 return @truncate(u4, @enumToInt(self));
6973 }
7074
75 /// Like id, but only returns the lower 3 bits.
76 pub fn low_id(self: Register) u3 {
77 return @truncate(u3, @enumToInt(self));
78 }
79
7180 /// Returns the index into `callee_preserved_regs`.
7281 pub fn allocIndex(self: Register) ?u4 {
7382 return switch (self) {
......@@ -136,6 +145,418 @@ pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8
136145pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
137146pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
138147
148/// Represents an unencoded x86 instruction.
149///
150/// Roughly based on the table headings at http://ref.x86asm.net/coder64.html
151pub const Instruction = struct {
152 /// Opcode prefix, needed for certain rare ops (e.g. MOVSS)
153 opcode_prefix: ?u8 = null,
154
155 /// One-byte primary opcode
156 primary_opcode_1b: ?u8 = null,
157 /// Two-byte primary opcode (always prefixed with 0f)
158 primary_opcode_2b: ?u8 = null,
159 // TODO: Support 3-byte opcodes
160
161 /// Secondary opcode
162 secondary_opcode: ?u8 = null,
163
164 /// Opcode extension (to be placed in the ModR/M byte in place of reg)
165 opcode_extension: ?u3 = null,
166
167 /// Legacy prefixes to use with this instruction
168 /// Most of the time, this field will be 0 and no prefixes are added.
169 /// Otherwise, a prefix will be added for each field set.
170 legacy_prefixes: LegacyPrefixes = .{},
171
172 /// 64-bit operand size
173 operand_size_64: bool = false,
174
175 /// The opcode-reg field,
176 /// stored in the 3 least significant bits of the opcode
177 /// on certain instructions + REX if extended
178 opcode_reg: ?Register = null,
179
180 /// The reg field
181 reg: ?Register = null,
182 /// The mod + r/m field
183 modrm: ?ModrmEffectiveAddress = null,
184 /// Location of the 3rd operand, if applicable
185 sib: ?SibEffectiveAddress = null,
186
187 /// Number of bytes of immediate
188 immediate_bytes: u8 = 0,
189 /// The value of the immediate
190 immediate: u64 = 0,
191
192 /// See legacy_prefixes
193 pub const LegacyPrefixes = packed struct {
194 /// LOCK
195 prefix_f0: bool = false,
196 /// REPNZ, REPNE, REP, Scalar Double-precision
197 prefix_f2: bool = false,
198 /// REPZ, REPE, REP, Scalar Single-precision
199 prefix_f3: bool = false,
200
201 /// CS segment override or Branch not taken
202 prefix_2e: bool = false,
203 /// DS segment override
204 prefix_36: bool = false,
205 /// ES segment override
206 prefix_26: bool = false,
207 /// FS segment override
208 prefix_64: bool = false,
209 /// GS segment override
210 prefix_65: bool = false,
211
212 /// Branch taken
213 prefix_3e: bool = false,
214
215 /// Operand size override
216 prefix_66: bool = false,
217
218 /// Address size override
219 prefix_67: bool = false,
220
221 padding: u5 = 0,
222 };
223
224 /// Encodes an effective address for the Mod + R/M part of the ModR/M byte
225 ///
226 /// Note that depending on the instruction, not all effective addresses are allowed.
227 ///
228 /// Examples:
229 /// eax: .reg = .eax
230 /// [eax]: .mem = .eax
231 /// [eax + 8]: .mem_disp = .{ .reg = .eax, .disp = 8 }
232 /// [eax - 8]: .mem_disp = .{ .reg = .eax, .disp = -8 }
233 /// [55]: .disp32 = 55
234 pub const ModrmEffectiveAddress = union(enum) {
235 reg: Register,
236 mem: Register,
237 mem_disp: struct {
238 reg: Register,
239 disp: i32,
240 },
241 disp32: u32,
242
243 pub fn isExtended(self: @This()) bool {
244 return switch (self) {
245 .reg => |reg| reg.isExtended(),
246 .mem => |memea| memea.isExtended(),
247 .mem_disp => |mem_disp| mem_disp.reg.isExtended(),
248 .disp32 => false,
249 };
250 }
251 };
252
253 /// Encodes an effective address for the SIB byte
254 ///
255 /// Note that depending on the instruction, not all effective addresses are allowed.
256 ///
257 /// Examples:
258 /// [eax + ebx * 2]: .base_index = .{ .base = .eax, .index = .ebx, .scale = 2 }
259 /// [eax]: .base_index = .{ .base = .eax, .index = null, .scale = 1 }
260 /// [ebx * 2 + 256]: .index_disp = .{ .index = .ebx, .scale = 2, .disp = 256 }
261 /// [[ebp] + ebx * 2 + 8]: .ebp_index_disp = .{ .index = .ebx, .scale = 2, .disp = 8 }
262 pub const SibEffectiveAddress = union(enum) {
263 base_index: struct {
264 base: Register,
265 index: ?Register,
266 scale: u8, // 1, 2, 4, or 8
267 },
268 index_disp: struct {
269 index: ?Register,
270 scale: u8, // 1, 2, 4, or 8
271 disp: u32,
272 },
273 ebp_index_disp: struct {
274 index: ?Register,
275 scale: u8, // 1, 2, 4, or 8
276 disp: u32,
277 },
278
279 pub fn baseIsExtended(self: @This()) bool {
280 return switch (self) {
281 .base_index => |base_index| base_index.base.isExtended(),
282 .index_disp, .ebp_index_disp => false,
283 };
284 }
285
286 pub fn indexIsExtended(self: @This()) bool {
287 return switch (self) {
288 .base_index => |base_index| if (base_index.index) |idx| idx.isExtended() else false,
289 .index_disp => |index_disp| if (index_disp.index) |idx| idx.isExtended() else false,
290 .ebp_index_disp => |ebp_index_disp| if (ebp_index_disp.index) |idx| idx.isExtended() else false,
291 };
292 }
293 };
294
295 /// Writes the encoded Instruction to the code ArrayList
296 pub fn encodeInto(inst: Instruction, code: *ArrayList(u8)) !void {
297 // We need to write the following, in that order:
298 // - Legacy prefixes (0 to 13 bytes)
299 // - REX prefix (0 to 1 byte)
300 // - Opcode (1, 2, or 3 bytes)
301 // - ModR/M (0 or 1 byte)
302 // - SIB (0 or 1 byte)
303 // - Displacement (0, 1, 2, or 4 bytes)
304 // - Immediate (0, 1, 2, 4, or 8 bytes)
305
306 // By this calculation, an instruction could be up to 31 bytes long (will probably not happen)
307 try code.ensureCapacity(code.items.len + 31);
308
309 // Legacy prefixes
310 if (@bitCast(u16, inst.legacy_prefixes) != 0) {
311 // Hopefully this path isn't taken very often, so we'll do it the slow way for now
312
313 // LOCK
314 if (inst.legacy_prefixes.prefix_f0) code.appendAssumeCapacity(0xf0);
315 // REPNZ, REPNE, REP, Scalar Double-precision
316 if (inst.legacy_prefixes.prefix_f2) code.appendAssumeCapacity(0xf2);
317 // REPZ, REPE, REP, Scalar Single-precision
318 if (inst.legacy_prefixes.prefix_f3) code.appendAssumeCapacity(0xf3);
319
320 // CS segment override or Branch not taken
321 if (inst.legacy_prefixes.prefix_2e) code.appendAssumeCapacity(0x2e);
322 // DS segment override
323 if (inst.legacy_prefixes.prefix_36) code.appendAssumeCapacity(0x36);
324 // ES segment override
325 if (inst.legacy_prefixes.prefix_26) code.appendAssumeCapacity(0x26);
326 // FS segment override
327 if (inst.legacy_prefixes.prefix_64) code.appendAssumeCapacity(0x64);
328 // GS segment override
329 if (inst.legacy_prefixes.prefix_65) code.appendAssumeCapacity(0x65);
330
331 // Branch taken
332 if (inst.legacy_prefixes.prefix_3e) code.appendAssumeCapacity(0x3e);
333
334 // Operand size override
335 if (inst.legacy_prefixes.prefix_66) code.appendAssumeCapacity(0x66);
336
337 // Address size override
338 if (inst.legacy_prefixes.prefix_67) code.appendAssumeCapacity(0x67);
339 }
340
341 // REX prefix
342 //
343 // A REX prefix has the following form:
344 // 0b0100_WRXB
345 // 0100: fixed bits
346 // W: stands for "wide", indicates that the instruction uses 64-bit operands.
347 // R, X, and B each contain the 4th bit of a register
348 // these have to be set when using registers 8-15.
349 // R: stands for "reg", extends the reg field in the ModR/M byte.
350 // X: stands for "index", extends the index field in the SIB byte.
351 // B: stands for "base", extends either the r/m field in the ModR/M byte,
352 // the base field in the SIB byte,
353 // or the opcode reg field in the Opcode byte.
354 {
355 var value: u8 = 0x40;
356 if (inst.opcode_reg) |opcode_reg| {
357 if (opcode_reg.isExtended()) {
358 value |= 0x1;
359 }
360 }
361 if (inst.modrm) |modrm| {
362 if (modrm.isExtended()) {
363 value |= 0x1;
364 }
365 }
366 if (inst.sib) |sib| {
367 if (sib.baseIsExtended()) {
368 value |= 0x1;
369 }
370 if (sib.indexIsExtended()) {
371 value |= 0x2;
372 }
373 }
374 if (inst.reg) |reg| {
375 if (reg.isExtended()) {
376 value |= 0x4;
377 }
378 }
379 if (inst.operand_size_64) {
380 value |= 0x8;
381 }
382 if (value != 0x40) {
383 code.appendAssumeCapacity(value);
384 }
385 }
386
387 // Opcode
388 if (inst.primary_opcode_1b) |opcode| {
389 var value = opcode;
390 if (inst.opcode_reg) |opcode_reg| {
391 value |= opcode_reg.low_id();
392 }
393 code.appendAssumeCapacity(value);
394 } else if (inst.primary_opcode_2b) |opcode| {
395 code.appendAssumeCapacity(0x0f);
396 var value = opcode;
397 if (inst.opcode_reg) |opcode_reg| {
398 value |= opcode_reg.low_id();
399 }
400 code.appendAssumeCapacity(value);
401 }
402
403 var disp8: ?u8 = null;
404 var disp16: ?u16 = null;
405 var disp32: ?u32 = null;
406
407 // ModR/M
408 //
409 // Example ModR/M byte:
410 // c7: ModR/M byte that contains:
411 // 11 000 111:
412 // ^ ^ ^
413 // mod | |
414 // reg |
415 // r/m
416 // where mod = 11 indicates that both operands are registers,
417 // reg = 000 indicates that the first operand is register EAX
418 // r/m = 111 indicates that the second operand is register EDI (since mod = 11)
419 if (inst.modrm != null or inst.reg != null or inst.opcode_extension != null) {
420 var value: u8 = 0;
421
422 // mod + rm
423 if (inst.modrm) |modrm| {
424 switch (modrm) {
425 .reg => |reg| {
426 value |= reg.low_id();
427 value |= 0b11_000_000;
428 },
429 .mem => |memea| {
430 assert(memea.low_id() != 4 and memea.low_id() != 5);
431 value |= memea.low_id();
432 // value |= 0b00_000_000;
433 },
434 .mem_disp => |mem_disp| {
435 assert(mem_disp.reg.low_id() != 4);
436 value |= mem_disp.reg.low_id();
437 if (mem_disp.disp < 128) {
438 // Use 1 byte of displacement
439 value |= 0b01_000_000;
440 disp8 = @bitCast(u8, @intCast(i8, mem_disp.disp));
441 } else {
442 // Use all 4 bytes of displacement
443 value |= 0b10_000_000;
444 disp32 = @bitCast(u32, mem_disp.disp);
445 }
446 },
447 .disp32 => |d| {
448 value |= 0b00_000_101;
449 disp32 = d;
450 },
451 }
452 }
453
454 // reg
455 if (inst.reg) |reg| {
456 value |= @as(u8, reg.low_id()) << 3;
457 } else if (inst.opcode_extension) |ext| {
458 value |= @as(u8, ext) << 3;
459 }
460
461 code.appendAssumeCapacity(value);
462 }
463
464 // SIB
465 {
466 if (inst.sib) |sib| {
467 return error.TODOSIBByteForX8664;
468 }
469 }
470
471 // Displacement
472 //
473 // The size of the displacement depends on the instruction used and is very fragile.
474 // The bytes are simply written in LE order.
475 {
476
477 // These writes won't fail because we ensured capacity earlier.
478 if (disp8) |d|
479 code.appendAssumeCapacity(d)
480 else if (disp16) |d|
481 mem.writeIntLittle(u16, code.addManyAsArrayAssumeCapacity(2), d)
482 else if (disp32) |d|
483 mem.writeIntLittle(u32, code.addManyAsArrayAssumeCapacity(4), d);
484 }
485
486 // Immediate
487 //
488 // The size of the immediate depends on the instruction used and is very fragile.
489 // The bytes are simply written in LE order.
490 {
491 // These writes won't fail because we ensured capacity earlier.
492 if (inst.immediate_bytes == 1)
493 code.appendAssumeCapacity(@intCast(u8, inst.immediate))
494 else if (inst.immediate_bytes == 2)
495 mem.writeIntLittle(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(u16, inst.immediate))
496 else if (inst.immediate_bytes == 4)
497 mem.writeIntLittle(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(u32, inst.immediate))
498 else if (inst.immediate_bytes == 8)
499 mem.writeIntLittle(u64, code.addManyAsArrayAssumeCapacity(8), inst.immediate);
500 }
501 }
502};
503
504fn expectEncoded(inst: Instruction, expected: []const u8) !void {
505 var code = ArrayList(u8).init(testing.allocator);
506 defer code.deinit();
507 try inst.encodeInto(&code);
508 testing.expectEqualSlices(u8, expected, code.items);
509}
510
511test "x86_64 Instruction.encodeInto" {
512 // simple integer multiplication
513
514 // imul eax,edi
515 // 0faf c7
516 try expectEncoded(Instruction{
517 .primary_opcode_2b = 0xaf, // imul
518 .reg = .eax, // destination
519 .modrm = .{ .reg = .edi }, // source
520 }, &[_]u8{ 0x0f, 0xaf, 0xc7 });
521
522 // simple mov
523
524 // mov eax,edi
525 // 89 f8
526 try expectEncoded(Instruction{
527 .primary_opcode_1b = 0x89, // mov (with rm as destination)
528 .reg = .edi, // source
529 .modrm = .{ .reg = .eax }, // destination
530 }, &[_]u8{ 0x89, 0xf8 });
531
532 // signed integer addition of 32-bit sign extended immediate to 64 bit register
533
534 // add rcx, 2147483647
535 //
536 // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding
537 //
538 // 48 : REX.W set for 64 bit operand (*r*cx)
539 // 81 : opcode for "<arithmetic> with immediate"
540 // c1 : id = rcx,
541 // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx)
542 // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD
543 // : 001 <-- 001 is rcx
544 // ffffff7f : 2147483647
545 try expectEncoded(Instruction{
546 // REX.W +
547 .operand_size_64 = true,
548 // 81
549 .primary_opcode_1b = 0x81,
550 // /0
551 .opcode_extension = 0,
552 // rcx
553 .modrm = .{ .reg = .rcx },
554 // immediate
555 .immediate_bytes = 4,
556 .immediate = 2147483647,
557 }, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f });
558}
559
139560// TODO add these registers to the enum and populate dwarfLocOp
140561// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
141562// RA = (16, "RA"),