756 lines
22 KiB
C
756 lines
22 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "third_party/chibicc/chibicc.h"
|
|
|
|
#define PRECIOUS 0b1111000000101000 // bx,bp,r12-r15
|
|
|
|
StaticAsm *staticasms;
|
|
|
|
static const char kGreg[4][16][5] = {
|
|
{"al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8b", "r9b", "r10b",
|
|
"r11b", "r12b", "r13b", "r14b", "r15b"},
|
|
{"ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w",
|
|
"r11w", "r12w", "r13w", "r14w", "r15w"},
|
|
{"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d",
|
|
"r10d", "r11d", "r12d", "r13d", "r14d", "r15d"},
|
|
{"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10",
|
|
"r11", "r12", "r13", "r14", "r15"},
|
|
};
|
|
|
|
static void DecodeAsmConstraints(AsmOperand *op) {
|
|
int i;
|
|
char c;
|
|
for (i = 0;;) {
|
|
switch ((c = op->str[i++])) {
|
|
case '\0':
|
|
case ',': // alternative group
|
|
return; // todo: read combos
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9': // reference
|
|
case '=': // output
|
|
case '+': // output and input
|
|
op->flow = c;
|
|
break;
|
|
case 'm': // memory
|
|
case 'o': // memory offsetable
|
|
op->type |= kAsmMem;
|
|
op->regmask |= 0b1111111111111111;
|
|
break;
|
|
case 'i': // int literal, c/asm constexpr, ld embedding
|
|
case 'n': // integer literal or compiler constexpr?
|
|
case 's': // integer constexpr but not literal (or known at link time?)
|
|
case 'M': // i∊[0,3] for index scaling, e.g. "mov\t(?,?,1<<%0),?"
|
|
case 'I': // i∊[0,31] 5 bits for 32-bit shifts
|
|
case 'J': // i∊[0,63] 6 bits for 64-bit shifts
|
|
case 'N': // i∊[0,255] in/out immediate byte
|
|
case 'K': // i∊[-128,127] signed byte integer
|
|
case 'Z': // i∊[0,2³²) for zero-extending
|
|
case 'L': // i∊{0xFF,0xFFFF,0xFFFFFFFF}
|
|
op->type |= kAsmImm;
|
|
break;
|
|
case 'a': // ax
|
|
op->regmask |= 0b0000000000000001;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'c': // cx
|
|
op->regmask |= 0b0000000000000010;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'd': // dx
|
|
op->regmask |= 0b0000000000000100;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'b': // bx
|
|
op->regmask |= 0b0000000000001000;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'S': // si
|
|
op->regmask |= 0b0000000001000000;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'D': // di
|
|
op->regmask |= 0b0000000010000000;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'r': // general register
|
|
op->regmask |= 0b1111111111111111;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'q': // greg lo-byte accessible
|
|
op->regmask |= 0b1111111111111111;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'Q': // greg hi-byte accessible
|
|
op->regmask |= 0b0000000000001111;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'U': // greg call-clobbered
|
|
op->regmask |= 0b0000111111000111;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'R': // greg all models
|
|
op->regmask |= 0b0000000011111111;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'l': // index register
|
|
op->regmask |= 0b1111111111101111;
|
|
op->type |= kAsmReg;
|
|
break;
|
|
case 'y': // mmx
|
|
op->type |= kAsmMmx;
|
|
op->regmask |= 0b0000000011111111;
|
|
break;
|
|
case 'x': // xmm
|
|
op->type |= kAsmXmm;
|
|
op->regmask |= 0b1111111111111111;
|
|
break;
|
|
case 'g': // rmi
|
|
op->type |= kAsmImm | kAsmMem | kAsmReg;
|
|
op->regmask |= 0b1111111111111111;
|
|
break;
|
|
case 'X': // anything
|
|
op->type |= kAsmImm | kAsmMem | kAsmReg | kAsmXmm | kAsmFpu | kAsmRaw;
|
|
op->regmask |= 0b1111111111111111;
|
|
op->x87mask |= 0b11111111;
|
|
break;
|
|
case 't': // %st
|
|
op->type |= kAsmFpu;
|
|
op->x87mask |= 0b00000001;
|
|
break;
|
|
case 'u': // %st(1)
|
|
op->type |= kAsmFpu;
|
|
op->x87mask |= 0b00000010;
|
|
break;
|
|
case 'f': // %st(0..7)
|
|
op->type |= kAsmFpu;
|
|
op->x87mask |= 0b11111111;
|
|
break;
|
|
case 'A': // ax+dx
|
|
error_tok(op->tok, "ax dx constraint not implemented");
|
|
case '@': // flags
|
|
if (op->flow != '=' || strlen(op->str + i) < 3 ||
|
|
(op->str[i] != 'c' || op->str[i + 1] != 'c')) {
|
|
error_tok(op->tok, "invalid flag constraint");
|
|
}
|
|
if (!is_integer(op->node->ty) || op->node->ty->size != 1) {
|
|
error_tok(op->node->tok, "invalid output flag type");
|
|
}
|
|
op->type = kAsmFlag;
|
|
op->predicate = i + 2;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsLvalue(AsmOperand *op) {
|
|
switch (op->node->kind) {
|
|
case ND_VAR:
|
|
case ND_DEREF:
|
|
case ND_MEMBER:
|
|
case ND_VLA_PTR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool CanUseReg(Node **n) {
|
|
if ((*n)->ty->kind == TY_ARRAY) {
|
|
*n = new_cast(*n, pointer_to((*n)->ty->base));
|
|
return true;
|
|
}
|
|
return is_integer((*n)->ty) || (*n)->ty->kind == TY_PTR;
|
|
}
|
|
|
|
static bool CanUseXmm(Node *n) {
|
|
return n->ty->vector_size == 16 || n->ty->kind == TY_FLOAT ||
|
|
n->ty->kind == TY_DOUBLE || n->ty->kind == TY_PTR ||
|
|
(n->ty->kind == TY_ARRAY && n->ty->size == 16);
|
|
}
|
|
|
|
static bool CanUseMmx(Node *n) {
|
|
return n->ty->kind == TY_FLOAT || n->ty->kind == TY_DOUBLE ||
|
|
n->ty->kind == TY_PTR || (n->ty->kind == TY_ARRAY && n->ty->size == 8);
|
|
}
|
|
|
|
static int PickAsmReferenceType(AsmOperand *op, AsmOperand *ref) {
|
|
switch (ref->type) {
|
|
case kAsmImm:
|
|
case kAsmMem:
|
|
error_tok(op->tok, "bad reference");
|
|
case kAsmReg:
|
|
if (!CanUseReg(&op->node)) {
|
|
error_tok(op->tok, "expected integral expression");
|
|
}
|
|
op->regmask = 0;
|
|
return ref->type;
|
|
case kAsmXmm:
|
|
if (!CanUseXmm(op->node)) {
|
|
error_tok(op->tok, "expected xmm expression");
|
|
}
|
|
return ref->type;
|
|
case kAsmFpu:
|
|
if (op->node->ty->kind != TY_LDOUBLE) {
|
|
error_tok(op->tok, "expected long double expression");
|
|
}
|
|
op->x87mask = 0;
|
|
return ref->type;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static int PickAsmOperandType(Asm *a, AsmOperand *op) {
|
|
if (op->flow == '=' || op->flow == '+') {
|
|
op->type &= ~kAsmImm;
|
|
if (!IsLvalue(op)) error_tok(op->tok, "lvalue required");
|
|
}
|
|
if ((op->type & kAsmImm) && is_const_expr(op->node)) {
|
|
op->val = eval2(op->node, &op->label);
|
|
return kAsmImm;
|
|
}
|
|
if ((op->type & kAsmMem) && op->node->ty->kind != TY_VOID) return kAsmMem;
|
|
if ((op->type & kAsmFpu) && op->node->ty->kind == TY_LDOUBLE) return kAsmFpu;
|
|
if ((op->type & kAsmXmm) && CanUseXmm(op->node)) return kAsmXmm;
|
|
if ((op->type & kAsmMmx) && CanUseMmx(op->node)) return kAsmMmx;
|
|
if ((op->type & kAsmReg) && CanUseReg(&op->node)) return kAsmReg;
|
|
if (op->type & kAsmFlag) return kAsmFlag;
|
|
if (op->type & kAsmRaw) return kAsmRaw;
|
|
error_tok(op->tok, "constraint mismatch");
|
|
}
|
|
|
|
static Token *ParseAsmOperand(Asm *a, AsmOperand *op, Token *tok) {
|
|
int i;
|
|
op->tok = tok;
|
|
op->str = ConsumeStringLiteral(&tok, tok);
|
|
tok = skip(tok, '(');
|
|
op->node = expr(&tok, tok);
|
|
add_type(op->node);
|
|
DecodeAsmConstraints(op);
|
|
if (isdigit(op->flow)) {
|
|
if ((i = op->flow - '0') >= a->n) error_tok(op->tok, "bad reference");
|
|
op->type = PickAsmReferenceType(op, a->ops + i);
|
|
} else {
|
|
op->type = PickAsmOperandType(a, op);
|
|
}
|
|
return skip(tok, ')');
|
|
}
|
|
|
|
static Token *ParseAsmOperands(Asm *a, Token *tok) {
|
|
if (EQUAL(tok, ":")) return tok;
|
|
for (;;) {
|
|
if (a->n == ARRAYLEN(a->ops)) {
|
|
error_tok(tok, "too many asm operands");
|
|
}
|
|
tok = ParseAsmOperand(a, &a->ops[a->n], tok);
|
|
++a->n;
|
|
if (!EQUAL(tok, ",")) break;
|
|
tok = skip(tok, ',');
|
|
}
|
|
return tok;
|
|
}
|
|
|
|
static void CouldNotAllocateRegister(AsmOperand *op) {
|
|
error_tok(op->tok, "could not allocate register");
|
|
}
|
|
|
|
static void PickAsmRegisters(Asm *a) {
|
|
int i, j, m, regset, xmmset, x87sts;
|
|
regset = 0b0000111111000111; // exclude bx,sp,bp,r12-r15
|
|
xmmset = 0b1111111111111111;
|
|
x87sts = 0b0000000011111111;
|
|
regset ^= regset & a->regclob; // don't allocate from clobber list
|
|
xmmset ^= xmmset & a->xmmclob;
|
|
x87sts ^= x87sts & a->x87clob;
|
|
for (j = 1; j <= 16; ++j) { // iterate from most to least restrictive
|
|
for (i = 0; i < a->n; ++i) {
|
|
switch (a->ops[i].type) {
|
|
case kAsmMem:
|
|
case kAsmReg:
|
|
if (!(m = a->ops[i].regmask)) break;
|
|
if (popcnt(m) != j) break;
|
|
if (!(m &= regset)) CouldNotAllocateRegister(&a->ops[i]);
|
|
regset &= ~(1 << (a->ops[i].reg = bsf(m)));
|
|
a->ops[i].regmask = 0;
|
|
break;
|
|
case kAsmXmm:
|
|
if (!(m = a->ops[i].regmask)) break;
|
|
if (!(m &= xmmset)) CouldNotAllocateRegister(&a->ops[i]);
|
|
xmmset &= ~(1 << (a->ops[i].reg = bsf(m)));
|
|
a->ops[i].regmask = 0;
|
|
break;
|
|
case kAsmFpu:
|
|
if (!(m = a->ops[i].x87mask)) break;
|
|
if (popcnt(m) != j) break;
|
|
if (!(m &= x87sts)) CouldNotAllocateRegister(&a->ops[i]);
|
|
x87sts &= ~(1 << (a->ops[i].reg = bsf(m)));
|
|
a->ops[i].x87mask = 0;
|
|
break;
|
|
default:
|
|
a->ops[i].regmask = 0;
|
|
a->ops[i].x87mask = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MarkUsedAsmOperands(Asm *a) {
|
|
char c, *p;
|
|
if (!a->isgnu) return;
|
|
for (p = a->str; (c = *p++);) {
|
|
if (c == '%') {
|
|
if (!(c = *p++)) error_tok(a->tok, "unexpected nul");
|
|
if (c == '%') continue;
|
|
if (!isdigit(c)) {
|
|
if (!(c = *p++)) error_tok(a->tok, "unexpected nul");
|
|
if (!isdigit(c)) {
|
|
error_tok(a->tok, "bad asm specifier");
|
|
}
|
|
}
|
|
a->ops[c - '0'].isused = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int GetIndexOfRegisterName(const char *s) {
|
|
int i, j;
|
|
for (i = 0; i < 16; ++i) {
|
|
for (j = 0; j < 4; ++j) {
|
|
if (!strcmp(s, kGreg[j][i])) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static Token *ParseAsmClobbers(Asm *a, Token *tok) {
|
|
int i;
|
|
char *s;
|
|
Token *stok;
|
|
for (;;) {
|
|
stok = tok;
|
|
s = ConsumeStringLiteral(&tok, tok);
|
|
if (*s == '%') ++s;
|
|
if (!strcmp(s, "cc")) {
|
|
a->flagclob = true;
|
|
} else if ((i = GetIndexOfRegisterName(s)) != -1) {
|
|
a->regclob |= 1 << i;
|
|
} else if (startswith(s, "xmm") && isdigit(s[3]) &&
|
|
(!s[4] || isdigit(s[4]))) {
|
|
i = s[3] - '0';
|
|
if (s[4]) {
|
|
i *= 10;
|
|
i += s[4] - '0';
|
|
}
|
|
i &= 15;
|
|
a->xmmclob |= 1 << i;
|
|
} else if (!strcmp(s, "st")) {
|
|
a->x87clob |= 1;
|
|
} else if (startswith(s, "st(") && isdigit(s[3]) && s[4] == ')') {
|
|
i = s[3] - '0';
|
|
i &= 7;
|
|
a->x87clob |= 1 << i;
|
|
} else if (!strcmp(s, "memory")) {
|
|
/* do nothing */
|
|
} else {
|
|
error_tok(stok, "unknown clobber register");
|
|
}
|
|
if (!EQUAL(tok, ",")) break;
|
|
tok = skip(tok, ',');
|
|
}
|
|
return tok;
|
|
}
|
|
|
|
// parses ansi c11 asm statement officially defined as follows
|
|
//
|
|
// asm-stmt = "asm" ("volatile" | "inline")* "(" string-literal ")"
|
|
//
|
|
// gnu c defines a notation for inputs, outputs, and clobbers, e.g.
|
|
//
|
|
// asm("foo %1,%0"
|
|
// : "=r"(x)
|
|
// : "r"(x)
|
|
// : "cc");
|
|
//
|
|
Asm *asm_stmt(Token **rest, Token *tok) {
|
|
Asm *a = calloc(1, sizeof(Asm));
|
|
tok = tok->next;
|
|
while (EQUAL(tok, "volatile") || EQUAL(tok, "inline")) tok = tok->next;
|
|
tok = skip(tok, '(');
|
|
a->tok = tok;
|
|
a->str = ConsumeStringLiteral(&tok, tok);
|
|
if (!EQUAL(tok, ")")) {
|
|
a->isgnu = true;
|
|
tok = skip(tok, ':');
|
|
tok = ParseAsmOperands(a, tok);
|
|
if (!EQUAL(tok, ")")) {
|
|
tok = skip(tok, ':');
|
|
tok = ParseAsmOperands(a, tok);
|
|
if (!EQUAL(tok, ")")) {
|
|
tok = skip(tok, ':');
|
|
tok = ParseAsmClobbers(a, tok);
|
|
}
|
|
}
|
|
}
|
|
PickAsmRegisters(a);
|
|
MarkUsedAsmOperands(a);
|
|
*rest = skip(tok, ')');
|
|
return a;
|
|
}
|
|
|
|
static void PrintAsmConstant(AsmOperand *op) {
|
|
if (op->label) {
|
|
fprintf(output_stream, "%s%+ld", *op->label, op->val);
|
|
} else {
|
|
fprintf(output_stream, "%ld", op->val);
|
|
}
|
|
}
|
|
|
|
static void EmitAsmSpecifier(AsmOperand *op, int q, int z) {
|
|
if (!q) {
|
|
switch (op->type) {
|
|
case kAsmImm:
|
|
fputc('$', output_stream);
|
|
PrintAsmConstant(op);
|
|
break;
|
|
case kAsmMem:
|
|
fprintf(output_stream, "(%%%s)", kGreg[3][op->reg]);
|
|
break;
|
|
case kAsmReg:
|
|
fprintf(output_stream, "%%%s", kGreg[z][op->reg]);
|
|
break;
|
|
case kAsmXmm:
|
|
fprintf(output_stream, "%%xmm%d", op->reg);
|
|
break;
|
|
case kAsmFpu:
|
|
fprintf(output_stream, "%%st(%d)", op->reg);
|
|
break;
|
|
case kAsmRaw:
|
|
fprintf(output_stream, "%.*s", op->node->tok->len, op->node->tok->loc);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
} else {
|
|
switch (q) {
|
|
case 'h': // hi byte
|
|
fprintf(output_stream, "%%%ch", "acdb"[op->reg]);
|
|
break;
|
|
case 'b': // lo byte
|
|
fprintf(output_stream, "%%%s", kGreg[0][op->reg]);
|
|
break;
|
|
case 'w': // word
|
|
fprintf(output_stream, "%%%s", kGreg[1][op->reg]);
|
|
break;
|
|
case 'k': // dword
|
|
fprintf(output_stream, "%%%s", kGreg[2][op->reg]);
|
|
break;
|
|
case 'q': // qword
|
|
fprintf(output_stream, "%%%s", kGreg[3][op->reg]);
|
|
break;
|
|
case 'z': // print suffix
|
|
fprintf(output_stream, "%c", "bwlq"[z]);
|
|
break;
|
|
case 'p': // print raw
|
|
fprintf(output_stream, "%.*s", op->node->tok->len, op->node->tok->loc);
|
|
break;
|
|
case 'a': // print address
|
|
PrintAsmConstant(op);
|
|
break;
|
|
case 'c': // print constant w/o punctuation
|
|
PrintAsmConstant(op);
|
|
break;
|
|
case 'P': // print w/ @plt
|
|
PrintAsmConstant(op);
|
|
fprintf(output_stream, "@plt");
|
|
break;
|
|
case 'l': // print label w/o punctuation
|
|
if (!op->label) {
|
|
error_tok(op->tok, "qualifier expected label");
|
|
}
|
|
fprintf(output_stream, "%s", *op->label);
|
|
break;
|
|
case 'V': // print register w/o punctuation
|
|
if (op->type != kAsmReg) {
|
|
error_tok(op->tok, "qualifier expected register");
|
|
}
|
|
fprintf(output_stream, "%s", kGreg[z][op->reg]);
|
|
break;
|
|
default:
|
|
error_tok(op->tok, "bad asm qualifier %%%`'c", q);
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *HandleAsmSpecifier(Asm *a, char *p) {
|
|
int c, i, q, z;
|
|
if (!(c = *p++)) error_tok(a->tok, "unexpected nul");
|
|
if (c == '%') {
|
|
fputc('%', output_stream);
|
|
return p;
|
|
}
|
|
if (c == '=') {
|
|
fprintf(output_stream, "%d", count());
|
|
return p;
|
|
}
|
|
if (isdigit(c)) {
|
|
q = '\0';
|
|
} else {
|
|
q = c;
|
|
if (!(c = *p++)) error_tok(a->tok, "unexpected nul");
|
|
if (!isdigit(c)) {
|
|
error_tok(a->tok, "bad asm specifier at offset %d", p - a->str);
|
|
}
|
|
}
|
|
if ((i = c - '0') >= a->n) {
|
|
error_tok(a->tok, "bad asm reference at offset %d", p - a->str);
|
|
}
|
|
z = bsr(a->ops[i].node->ty->size);
|
|
if (z > 3 && a->ops[i].type == kAsmReg) {
|
|
error_tok(a->tok, "bad asm op size");
|
|
}
|
|
EmitAsmSpecifier(&a->ops[i], q, z);
|
|
return p;
|
|
}
|
|
|
|
static void EmitAsmText(Asm *a) {
|
|
char c, *p;
|
|
if (*a->str) {
|
|
if (a->isgnu) {
|
|
flushln();
|
|
fprintf(output_stream, "\t");
|
|
for (p = a->str;;) {
|
|
switch ((c = *p++)) {
|
|
case '\0':
|
|
fputc('\n', output_stream);
|
|
return;
|
|
case '%':
|
|
p = HandleAsmSpecifier(a, p);
|
|
break;
|
|
default:
|
|
fputc(c, output_stream);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
println("\t%s", a->str);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PushAsmInput(AsmOperand *op) {
|
|
gen_expr(op->node);
|
|
push();
|
|
}
|
|
|
|
static void PushAsmInputs(Asm *a) {
|
|
int i;
|
|
for (i = 0; i < a->n; ++i) {
|
|
if (a->ops[i].flow == '=') continue;
|
|
switch (a->ops[i].type) {
|
|
case kAsmReg:
|
|
PushAsmInput(&a->ops[i]);
|
|
break;
|
|
case kAsmMem:
|
|
if (a->ops[i].isused) {
|
|
PushAsmInput(&a->ops[i]);
|
|
}
|
|
break;
|
|
case kAsmXmm:
|
|
gen_expr(a->ops[i].node);
|
|
println("\tsub\t$16,%%rsp");
|
|
switch (a->ops[i].node->ty->kind) {
|
|
case TY_FLOAT:
|
|
case TY_DOUBLE:
|
|
println("\tmovdqu\t%%xmm0,(%%rsp)");
|
|
break;
|
|
default:
|
|
println("\tmovdqu\t(%%rax),%%xmm0");
|
|
println("\tmovdqu\t%%xmm0,(%%rsp)");
|
|
break;
|
|
}
|
|
break;
|
|
case kAsmMmx:
|
|
gen_expr(a->ops[i].node);
|
|
println("\tsub\t$8,%%rsp");
|
|
switch (a->ops[i].node->ty->kind) {
|
|
case TY_FLOAT:
|
|
case TY_DOUBLE:
|
|
println("\tmovq\t%%mm0,(%%rsp)");
|
|
break;
|
|
default:
|
|
println("\tmovq\t(%%rax),%%mm0");
|
|
println("\tmovq\t%%mm0,(%%rsp)");
|
|
break;
|
|
}
|
|
break;
|
|
case kAsmFpu: /* TODO: How does this work in non-simple case? */
|
|
gen_expr(a->ops[i].node);
|
|
println("\tsub\t$16,%%rsp");
|
|
println("\tfstpt\t(%%rsp)");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PopAsmInput(Asm *a, AsmOperand *op) {
|
|
if (isdigit(op->flow)) {
|
|
popreg(kGreg[3][a->ops[op->flow - '0'].reg]);
|
|
} else {
|
|
popreg(kGreg[3][op->reg]);
|
|
}
|
|
}
|
|
|
|
static void PopAsmInputs(Asm *a) {
|
|
int i;
|
|
for (i = a->n; i--;) {
|
|
if (a->ops[i].flow == '=') continue;
|
|
switch (a->ops[i].type) {
|
|
case kAsmReg:
|
|
PopAsmInput(a, &a->ops[i]);
|
|
break;
|
|
case kAsmMem:
|
|
if (a->ops[i].isused) {
|
|
PopAsmInput(a, &a->ops[i]);
|
|
}
|
|
break;
|
|
case kAsmXmm:
|
|
println("\tmovdqu\t(%%rsp),%%xmm%d", a->ops[i].reg);
|
|
println("\tadd\t$16,%%rsp");
|
|
break;
|
|
case kAsmMmx:
|
|
println("\tmovq\t(%%rsp),%%mm%d", a->ops[i].reg);
|
|
println("\tadd\t$8,%%rsp");
|
|
break;
|
|
case kAsmFpu: /* TODO: How does this work in non-simple case? */
|
|
println("\tfldt\t(%%rsp)");
|
|
println("\tadd\t$16,%%rsp");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void StoreAsmOutputs(Asm *a) {
|
|
int i, z, x0, x1;
|
|
for (i = 0; i < a->n; ++i) {
|
|
if (a->ops[i].flow == '=' || a->ops[i].flow == '+') {
|
|
switch (a->ops[i].type) {
|
|
case kAsmFlag:
|
|
gen_addr(a->ops[i].node);
|
|
println("\tset%s\t(%%rax)", a->ops[i].str + a->ops[i].predicate);
|
|
break;
|
|
case kAsmReg:
|
|
z = bsr(a->ops[i].node->ty->size);
|
|
if (a->ops[i].reg) {
|
|
gen_addr(a->ops[i].node);
|
|
if (z > 3) error_tok(a->tok, "bad asm out size");
|
|
println("\tmov\t%%%s,(%%rax)", kGreg[z][a->ops[i].reg]);
|
|
} else {
|
|
println("\tpush\t%%rbx");
|
|
push();
|
|
pop("%rbx");
|
|
gen_addr(a->ops[i].node);
|
|
println("\tmov\t%%%s,(%%rax)", kGreg[z][3]);
|
|
println("\tpop\t%%rbx");
|
|
}
|
|
break;
|
|
case kAsmXmm:
|
|
gen_addr(a->ops[i].node);
|
|
switch (a->ops[i].node->ty->kind) {
|
|
case TY_FLOAT:
|
|
println("\tmovss\t%%xmm%d,(%%rax)", a->ops[i].reg);
|
|
break;
|
|
case TY_DOUBLE:
|
|
println("\tmovsd\t%%xmm%d,(%%rax)", a->ops[i].reg);
|
|
break;
|
|
default:
|
|
println("\tmovdqu\t%%xmm%d,(%%rax)", a->ops[i].reg);
|
|
break;
|
|
}
|
|
break;
|
|
case kAsmMmx:
|
|
gen_addr(a->ops[i].node);
|
|
switch (a->ops[i].node->ty->kind) {
|
|
case TY_FLOAT:
|
|
println("\tmovss\t%%mm%d,(%%rax)", a->ops[i].reg);
|
|
break;
|
|
case TY_DOUBLE:
|
|
println("\tmovsd\t%%mm%d,(%%rax)", a->ops[i].reg);
|
|
break;
|
|
default:
|
|
println("\tmovq\t%%mm%d,(%%rax)", a->ops[i].reg);
|
|
break;
|
|
}
|
|
break;
|
|
case kAsmFpu: /* TODO: How does this work in non-simple case? */
|
|
gen_addr(a->ops[i].node);
|
|
println("\tfstpt\t(%%rax)");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PushClobbers(Asm *a) {
|
|
int i, regs = a->regclob & PRECIOUS;
|
|
while (regs) {
|
|
i = bsf(regs);
|
|
pushreg(kGreg[3][i]);
|
|
regs &= ~(1 << i);
|
|
}
|
|
}
|
|
|
|
static void PopClobbers(Asm *a) {
|
|
int i, regs = a->regclob & PRECIOUS;
|
|
while (regs) {
|
|
i = bsr(regs);
|
|
popreg(kGreg[3][i]);
|
|
regs &= ~(1 << i);
|
|
}
|
|
}
|
|
|
|
// generates shocking horrible code for parsed asm statement
|
|
void gen_asm(Asm *a) {
|
|
PushAsmInputs(a);
|
|
print_loc(a->tok->file->file_no, a->tok->line_no);
|
|
PopAsmInputs(a);
|
|
PushClobbers(a);
|
|
EmitAsmText(a);
|
|
StoreAsmOutputs(a);
|
|
PopClobbers(a);
|
|
}
|