Files
triton/lib/lang/wgtcc/code_gen.cc

1562 lines
40 KiB
C++
Raw Normal View History

2019-08-19 20:56:39 -07:00
#include "triton/lang/wgtcc/code_gen.h"
#include "triton/lang/wgtcc/evaluator.h"
#include "triton/lang/wgtcc/parser.h"
#include "triton/lang/wgtcc/token.h"
#include <cstdarg>
#include <queue>
#include <set>
extern std::string filename_in;
extern std::string filename_out;
extern bool debug;
const std::string* Generator::last_file = nullptr;
Parser* Generator::parser_ = nullptr;
FILE* Generator::outFile_ = nullptr;
RODataList Generator::rodatas_;
std::vector<Declaration*> Generator::staticDecls_;
int Generator::offset_ = 0;
int Generator::retAddrOffset_ = 0;
FuncDef* Generator::curFunc_ = nullptr;
/*
* Register usage:
* xmm0: accumulator of floating datas;
* xmm8: temp register for param passing(xmm0)
* xmm9: source operand register;
* xmm10: tmp register for floating data swap;
* rax: accumulator;
* r12, r13: temp register for rdx and rcx
* r11: source operand register;
* r10: base register when LValGenerator eval the address.
* rcx: tempvar register, like the tempvar of 'switch'
* temp register for struct copy
*/
static std::vector<const char*> regs {
"%rdi", "%rsi", "%rdx",
"%rcx", "%r8", "%r9"
};
static std::vector<const char*> xregs {
"%xmm0", "%xmm1", "%xmm2", "%xmm3",
"%xmm4", "%xmm5", "%xmm6", "%xmm7"
};
static ParamClass Classify(Type* paramType, int offset=0) {
if (paramType->IsInteger() || paramType->ToPointer()
|| paramType->ToArray()) {
return ParamClass::INTEGER;
}
if (paramType->ToArithm()) {
auto type = paramType->ToArithm();
if (type->Tag() == T_FLOAT || type->Tag() == T_DOUBLE)
return ParamClass::SSE;
if (type->Tag() == (T_LONG | T_DOUBLE)) {
// TODO(wgtdkp):
return ParamClass::SSE;
assert(false);
return ParamClass::X87;
}
// TODO(wgtdkp):
assert(false);
// It is complex
if ((type->Tag() & T_LONG) && (type->Tag() & T_DOUBLE))
return ParamClass::COMPLEX_X87;
}
auto type = paramType->ToStruct();
assert(type);
return ParamClass::MEMORY;
// TODO(wgtdkp): Support agrregate type
assert(false);
/*
auto type = paramType->ToStruct();
assert(type);
if (type->Width() > 4 * 8)
return PC_MEMORY;
std::vector<ParamClass> classes;
int cnt = (type->Width() + 7) / 8;
for (int i = 0; i < cnt; ++i) {
auto types = FieldsIn8Bytes(type, i);
assert(types.size() > 0);
auto fieldClass = (types.size() == 1)
? PC_NO_CLASS: FieldClass(types, 0);
classes.push_back(fieldClass);
}
bool sawX87 = false;
for (int i = 0; i < classes.size(); ++i) {
if (classes[i] == PC_MEMORY)
return PC_MEMORY;
if (classes[i] == PC_X87_UP && sawX87)
return PC_MEMORY;
if (classes[i] == PC_X87)
sawX87 = true;
}
*/
return ParamClass::NO_CLASS; // Make compiler happy
}
std::string Generator::ConsLabel(Constant* cons) {
if (cons->Type()->IsInteger()) {
return "$" + std::to_string(cons->IVal());
} else if (cons->Type()->IsFloat()) {
double valsd = cons->FVal();
float valss = valsd;
// TODO(wgtdkp): Add rodata
auto width = cons->Type()->Width();
long val = (width == 4)? *reinterpret_cast<int*>(&valss):
*reinterpret_cast<long*>(&valsd);
const ROData& rodata = ROData(val, width);
rodatas_.push_back(rodata);
return rodata.label_;
} else { // Literal
const ROData& rodata = ROData(cons->SValRepr());
rodatas_.push_back(rodata);
return rodata.label_; // Return address
}
}
static const char* GetLoad(int width, bool flt=false) {
switch (width) {
case 1: return "movzbq";
case 2: return "movzwq";
case 4: return !flt ? "movl": "movss";
case 8: return !flt ? "movq": "movsd";
default: assert(false); return nullptr;
}
}
static std::string GetInst(const std::string& inst, int width, bool flt) {
if (flt) {
return inst + (width == 4 ? "ss": "sd");
} else {
switch (width) {
case 1: return inst + "b";
case 2: return inst + "w";
case 4: return inst + "l";
case 8: return inst + "q";
default: assert(false);
}
return inst; // Make compiler happy
}
}
static std::string GetInst(const std::string& inst, Type* type) {
assert(type->IsScalar());
return GetInst(inst, type->Width(), type->IsFloat());
}
static std::string GetReg(int width) {
switch (width) {
case 1: return "%al";
case 2: return "%ax";
case 4: return "%eax";
case 8: return "%rax";
default: assert(false); return "";
}
}
static std::string GetDes(int width, bool flt) {
if (flt) {
return "%xmm0";
}
return GetReg(width);
}
static std::string GetSrc(int width, bool flt) {
if (flt) {
return "%xmm9";
}
switch (width) {
case 1: return "%r11b";
case 2: return "%r11w";
case 4: return "%r11d";
case 8: return "%r11";
default: assert(false); return "";
}
}
// The 'reg' always be 8 bytes
int Generator::Push(const std::string& reg) {
offset_ -= 8;
auto mov = reg[1] == 'x' ? "movsd": "movq";
Emit(mov, reg, ObjectAddr(offset_));
return offset_;
}
int Generator::Push(Type* type) {
if (type->IsFloat()) {
return Push("%xmm0");
} else if (type->IsScalar()) {
return Push("%rax");
} else {
offset_ -= type->Width();
offset_ = Type::MakeAlign(offset_, 8);
CopyStruct({"", "%rbp", offset_}, type->Width());
return offset_;
}
}
// The 'reg' must be 8 bytes
int Generator::Pop(const std::string& reg) {
auto mov = reg[1] == 'x' ? "movsd": "movq";
Emit(mov, ObjectAddr(offset_), reg);
offset_ += 8;
return offset_;
}
void Generator::Spill(bool flt) {
Push(flt ? "%xmm0": "%rax");
}
void Generator::Restore(bool flt) {
const auto& src = GetSrc(8, flt);
const auto& des = GetDes(8, flt);
const auto& inst = GetInst("mov", 8, flt);
Emit(inst, des, src);
Pop(des);
}
void Generator::Save(bool flt) {
if (flt) {
Emit("movsd", "%xmm0", "%xmm9");
} else {
Emit("movq", "%rax", "%r11");
}
}
/*
* Operator/Instruction mapping:
* + add
* - sub
* * mul
* / div
* % div
* << sal
* >> sar
* | or
* & and
* ^ xor
* = mov
* < cmp, setl, movzbq
* > cmp, setg, movzbq
* <= cmp, setle, movzbq
* >= cmp, setle, movzbq
* == cmp, sete, movzbq
* != cmp, setne, movzbq
* && GenAndOp
* || GenOrOp
* ] GenSubScriptingOp
* . GenMemberRefOp
*/
void Generator::VisitBinaryOp(BinaryOp* binary) {
EmitLoc(binary);
auto op = binary->op_;
if (op == '=')
return GenAssignOp(binary);
if (op == Token::LOGICAL_AND)
return GenAndOp(binary);
if (op == Token::LOGICAL_OR)
return GenOrOp(binary);
if (op == '.')
return GenMemberRefOp(binary);
if (op == ',')
return GenCommaOp(binary);
// Why lhs_->Type() ?
// Because, the type of pointer subtraction is arithmetic type
if (binary->lhs_->Type()->ToPointer() &&
(op == '+' || op == '-')) {
return GenPointerArithm(binary);
}
// Careful: for compare operator, the type of the expression
// is always integer, while the type of lhs and rhs could be float
// After convertion, lhs and rhs always has the same type
auto type = binary->lhs_->Type();
auto width = type->Width();
auto flt = type->IsFloat();
auto sign = !type->IsUnsigned();
Visit(binary->lhs_);
Spill(flt);
Visit(binary->rhs_);
Restore(flt);
const char* inst = nullptr;
switch (op) {
case '*': return GenMulOp(width, flt, sign);
case '/': case '%': return GenDivOp(flt, sign, width, op);
case '<':
return GenCompOp(width, flt, (flt || !sign) ? "setb": "setl");
case '>':
return GenCompOp(width, flt, (flt || !sign) ? "seta": "setg");
case Token::LE:
return GenCompOp(width, flt, (flt || !sign) ? "setbe": "setle");
case Token::GE:
return GenCompOp(width, flt, (flt || !sign) ? "setae": "setge");
case Token::EQ:
return GenCompOp(width, flt, "sete");
case Token::NE:
return GenCompOp(width, flt, "setne");
case '+': inst = "add"; break;
case '-': inst = "sub"; break;
case '|': inst = "or"; break;
case '&': inst = "and"; break;
case '^': inst = "xor"; break;
case Token::LEFT: case Token::RIGHT:
inst = op == Token::LEFT ? "sal": (sign ? "sar": "shr");
Emit("movq %r11, %rcx");
Emit(GetInst(inst, width, flt), "%cl", GetDes(width, flt));
return;
}
Emit(GetInst(inst, width, flt), GetSrc(width, flt), GetDes(width, flt));
}
void Generator::GenCommaOp(BinaryOp* comma) {
VisitExpr(comma->lhs_);
VisitExpr(comma->rhs_);
}
void Generator::GenMulOp(int width, bool flt, bool sign) {
auto inst = flt ? "mul": (sign ? "imul": "mul");
if (flt) {
Emit(GetInst(inst, width, flt), "%xmm9", "%xmm0");
} else {
Emit(GetInst(inst, width, flt), GetSrc(width, flt));
}
}
void Generator::GenCompZero(Type* type) {
auto width = type->Width();
auto flt = type->IsFloat();
if (!flt) {
Emit("cmp", "$0", GetReg(width));
} else {
Emit("pxor", "%xmm9", "%xmm9");
auto cmp = width == 8 ? "ucomisd": "ucomiss";
Emit(cmp, "%xmm9", "%xmm0");
}
}
void Generator::GenAndOp(BinaryOp* andOp) {
VisitExpr(andOp->lhs_);
GenCompZero(andOp->lhs_->Type());
auto labelFalse = LabelStmt::New();
Emit("je", labelFalse);
VisitExpr(andOp->rhs_);
GenCompZero(andOp->rhs_->Type());
Emit("je", labelFalse);
Emit("movq", "$1", "%rax");
auto labelTrue = LabelStmt::New();
Emit("jmp", labelTrue);
EmitLabel(labelFalse->Repr());
Emit("xorq", "%rax", "%rax"); // Set %rax to 0
EmitLabel(labelTrue->Repr());
}
void Generator::GenOrOp(BinaryOp* orOp) {
VisitExpr(orOp->lhs_);
GenCompZero(orOp->lhs_->Type());
auto labelTrue = LabelStmt::New();
Emit("jne", labelTrue);
VisitExpr(orOp->rhs_);
GenCompZero(orOp->rhs_->Type());
Emit("jne", labelTrue);
Emit("xorq", "%rax", "%rax"); // Set %rax to 0
auto labelFalse = LabelStmt::New();
Emit("jmp", labelFalse);
EmitLabel(labelTrue->Repr());
Emit("movq", "$1", "%rax");
EmitLabel(labelFalse->Repr());
}
void Generator::GenMemberRefOp(BinaryOp* ref) {
// As the lhs will always be struct/union
auto addr = LValGenerator().GenExpr(ref->lhs_);
const auto& name = ref->rhs_->Tok()->str_;
auto structType = ref->lhs_->Type()->ToStruct();
auto member = structType->GetMember(name);
addr.offset_ += member->Offset();
if (!ref->Type()->IsScalar()) {
Emit("leaq", addr, "%rax");
} else {
if (member->BitFieldWidth()) {
EmitLoadBitField(addr.Repr(), member);
} else {
EmitLoad(addr.Repr(), ref->Type());
}
}
}
void Generator::EmitLoadBitField(const std::string& addr, Object* bitField) {
auto type = bitField->Type()->ToArithm();
assert(type && type->IsInteger());
EmitLoad(addr, type);
Emit("andq", Object::BitFieldMask(bitField), "%rax");
auto shiftRight = (type->Tag() & T_UNSIGNED) ? "shrq": "sarq";
auto left = 64 - bitField->bitFieldBegin_ - bitField->bitFieldWidth_;
auto right = 64 - bitField->bitFieldWidth_;
Emit("salq", left, "%rax");
Emit(shiftRight, right, "%rax");
}
// FIXME(wgtdkp): for combined assignment operator, if the rvalue expr
// has some side-effect, the rvalue will be evaluated twice!
void Generator::GenAssignOp(BinaryOp* assign) {
// The base register of addr is %r10, %rip, %rbp
auto addr = LValGenerator().GenExpr(assign->lhs_);
// Base register of static object maybe %rip
// Visit rhs_ may changes r10
if (addr.base_ == "%r10")
Push(addr.base_);
VisitExpr(assign->rhs_);
if (addr.base_ == "%r10")
Pop(addr.base_);
if (assign->Type()->IsScalar()) {
EmitStore(addr, assign->Type());
} else {
// struct/union type
// The address of rhs is in %rax
CopyStruct(addr, assign->Type()->Width());
}
}
void Generator::EmitStoreBitField(const ObjectAddr& addr, Type* type) {
auto arithmType = type->ToArithm();
assert(arithmType && arithmType->IsInteger());
// The value to be stored is in %rax now
auto mask = Object::BitFieldMask(addr.bitFieldBegin_, addr.bitFieldWidth_);
Emit("salq", addr.bitFieldBegin_, "%rax");
Emit("andq", mask, "%rax");
Emit("movq", "%rax", "%r11");
EmitLoad(addr.Repr(), arithmType);
Emit("andq", ~mask, "%rax");
Emit("orq", "%r11", "%rax");
EmitStore(addr.Repr(), type);
}
void Generator::CopyStruct(ObjectAddr desAddr, int width) {
int units[] = {8, 4, 2, 1};
Emit("movq", "%rax", "%rcx");
ObjectAddr srcAddr = {"", "%rcx", 0};
for (auto unit: units) {
while (width >= unit) {
EmitLoad(srcAddr.Repr(), unit, false);
EmitStore(desAddr.Repr(), unit, false);
desAddr.offset_ += unit;
srcAddr.offset_ += unit;
width -= unit;
}
}
}
void Generator::GenCompOp(int width, bool flt, const char* set) {
std::string cmp;
if (flt) {
cmp = width == 8 ? "ucomisd": "ucomiss";
} else {
cmp = GetInst("cmp", width, flt);
}
Emit(cmp, GetSrc(width, flt), GetDes(width, flt));
Emit(set, "%al");
Emit("movzbq", "%al", "%rax");
}
void Generator::GenDivOp(bool flt, bool sign, int width, int op) {
if (flt) {
auto inst = width == 4 ? "divss": "divsd";
Emit(inst, "%xmm9", "%xmm0");
return;
}
if (!sign) {
Emit("xor", "%rdx", "%rdx");
Emit(GetInst("div", width, flt), GetSrc(width, flt));
} else {
Emit(width == 4 ? "cltd": "cqto");
Emit(GetInst("idiv", width, flt), GetSrc(width, flt));
}
if (op == '%')
Emit("movq", "%rdx", "%rax");
}
void Generator::GenPointerArithm(BinaryOp* binary) {
assert(binary->op_ == '+' || binary->op_ == '-');
// For '+', we have swapped lhs_ and rhs_ to ensure that
// the pointer is at lhs.
Visit(binary->lhs_);
Spill(false);
Visit(binary->rhs_);
Restore(false);
auto type = binary->lhs_->Type()->ToPointer()->Derived();
auto width = type->Width();
if (binary->op_ == '+') {
if (width > 1)
Emit("imulq", width, "%r11");
Emit("addq", "%r11", "%rax");
} else {
Emit("subq", "%r11", "%rax");
if (width > 1) {
Emit("movq", width, "%r11");
GenDivOp(false, true, 8, '/');
}
}
}
// Only objects Allocated on stack
void Generator::VisitObject(Object* obj) {
EmitLoc(obj);
auto addr = LValGenerator().GenExpr(obj).Repr();
if (!obj->Type()->IsScalar()) {
// Return the address of the object in rax
Emit("leaq", addr, "%rax");
} else {
EmitLoad(addr, obj->Type());
}
}
void Generator::GenCastOp(UnaryOp* cast) {
auto desType = cast->Type();
auto srcType = cast->operand_->Type();
if (srcType->IsFloat() && desType->IsFloat()) {
if (srcType->Width() == desType->Width())
return;
auto inst = srcType->Width() == 4 ? "cvtss2sd": "cvtsd2ss";
Emit(inst, "%xmm0", "%xmm0");
} else if (srcType->IsFloat()) {
// Handle bool
if (desType->IsBool()) {
Emit("pxor", "%xmm9", "%xmm9");
GenCompOp(srcType->Width(), true, "setne");
} else {
auto inst = srcType->Width() == 4 ? "cvttss2si": "cvttsd2si";
Emit(inst, "%xmm0", "%rax");
}
} else if (desType->IsFloat()) {
auto inst = desType->Width() == 4 ? "cvtsi2ss": "cvtsi2sd";
Emit(inst, "%rax", "%xmm0");
} else if (srcType->ToPointer()
|| srcType->ToFunc()
|| srcType->ToArray()) {
// Handle bool
if (desType->IsBool()) {
Emit("testq", "%rax", "%rax");
Emit("setne", "%al");
}
} else {
assert(srcType->ToArithm());
int width = srcType->Width();
auto sign = !srcType->IsUnsigned();
const char* inst;
switch (width) {
case 1:
inst = sign ? "movsbq": "movzbq";
Emit(inst, GetReg(width), "%rax");
break;
case 2:
inst = sign ? "movswq": "movzwq";
Emit(inst, GetReg(width), "%rax");
break;
case 4: inst = "movl";
if (desType->Width() == 8)
Emit("cltq");
break;
case 8: break;
}
// Handle bool
if (desType->IsBool()) {
Emit("testq", "%rax", "%rax");
Emit("setne", "%al");
}
}
}
void Generator::VisitUnaryOp(UnaryOp* unary) {
EmitLoc(unary);
switch (unary->op_) {
case Token::PREFIX_INC:
return GenIncDec(unary->operand_, false, "add");
case Token::PREFIX_DEC:
return GenIncDec(unary->operand_, false, "sub");
case Token::POSTFIX_INC:
return GenIncDec(unary->operand_, true, "add");
case Token::POSTFIX_DEC:
return GenIncDec(unary->operand_, true, "sub");
case Token::ADDR: {
auto addr = LValGenerator().GenExpr(unary->operand_).Repr();
Emit("leaq", addr, "%rax");
} return;
case Token::DEREF:
return GenDerefOp(unary);
case Token::PLUS:
return VisitExpr(unary->operand_);
case Token::MINUS:
return GenMinusOp(unary);
case '~':
VisitExpr(unary->operand_);
return Emit("notq", "%rax");
case '!':
VisitExpr(unary->operand_);
GenCompZero(unary->operand_->Type());
Emit("sete", "%al");
Emit("movzbl", "%al", "%eax"); // Type of !operator is int
return;
case Token::CAST:
Visit(unary->operand_);
GenCastOp(unary);
return;
default: assert(false);
}
}
void Generator::GenDerefOp(UnaryOp* deref) {
VisitExpr(deref->operand_);
if (deref->Type()->IsScalar()) {
ObjectAddr addr {"", "%rax", 0};
EmitLoad(addr.Repr(), deref->Type());
} else {
// Just let it go!
}
}
void Generator::GenMinusOp(UnaryOp* minus) {
auto width = minus->Type()->Width();
auto flt = minus->Type()->IsFloat();
VisitExpr(minus->operand_);
if (flt) {
Emit("pxor", "%xmm9", "%xmm9");
Emit(GetInst("sub", width, flt), "%xmm0", "%xmm9");
Emit(GetInst("mov", width, flt), "%xmm9", "%xmm0");
} else {
Emit(GetInst("neg", width, flt), GetDes(width, flt));
}
}
void Generator::GenIncDec(Expr* operand,
bool postfix,
const std::string& inst) {
auto width = operand->Type()->Width();
auto flt = operand->Type()->IsFloat();
auto addr = LValGenerator().GenExpr(operand).Repr();
EmitLoad(addr, operand->Type());
if (postfix) Save(flt);
Constant* cons;
auto pointerType = operand->Type()->ToPointer();
if (pointerType) {
long width = pointerType->Derived()->Width();
cons = Constant::New(operand->Tok(), T_LONG, width);
} else if (operand->Type()->IsInteger()) {
cons = Constant::New(operand->Tok(), T_LONG, 1L);
} else {
if (width == 4)
cons = Constant::New(operand->Tok(), T_FLOAT, 1.0f);
else
cons = Constant::New(operand->Tok(), T_DOUBLE, 1.0);
}
Emit(GetInst(inst, operand->Type()), ConsLabel(cons), GetDes(width, flt));
EmitStore(addr, operand->Type());
if (postfix && flt) {
Emit("movsd", "%xmm9", "%xmm0");
} else if (postfix) {
Emit("mov", "%r11", "%rax");
}
}
void Generator::VisitConditionalOp(ConditionalOp* condOp) {
EmitLoc(condOp);
auto ifStmt = IfStmt::New(condOp->cond_,
condOp->exprTrue_, condOp->exprFalse_);
VisitIfStmt(ifStmt);
}
void Generator::VisitEnumerator(Enumerator* enumer) {
EmitLoc(enumer);
auto cons = Constant::New(enumer->Tok(), T_INT, (long)enumer->Val());
Visit(cons);
}
// Ident must be function
void Generator::VisitIdentifier(Identifier* ident) {
EmitLoc(ident);
Emit("leaq", ident->Name(), "%rax");
}
void Generator::VisitConstant(Constant* cons) {
EmitLoc(cons);
auto label = ConsLabel(cons);
if (!cons->Type()->IsScalar()) {
Emit("leaq", label, "%rax");
} else {
auto width = cons->Type()->Width();
auto flt = cons->Type()->IsFloat();
auto load = GetInst("mov", width, flt);
auto des = GetDes(width, flt);
Emit(load, label, des);
}
}
// Use %ecx as temp register
// TempVar is only used for condition expression of 'switch'
// and struct copy
void Generator::VisitTempVar(TempVar* tempVar) {
assert(tempVar->Type()->IsInteger());
Emit("movl", "%ecx", "%eax");
}
void Generator::VisitDeclaration(Declaration* decl) {
EmitLoc(decl->obj_);
auto obj = decl->obj_;
if (!obj->IsStatic()) {
// The object has no linkage and has
// no static storage(the object is on stack).
// If it has no initialization,
// then it's value is random initialized.
if (!obj->HasInit())
return;
int lastEnd = obj->Offset();
for (const auto& init: decl->Inits()) {
ObjectAddr addr = ObjectAddr(obj->Offset() + init.offset_);
addr.bitFieldBegin_ = init.bitFieldBegin_;
addr.bitFieldWidth_ = init.bitFieldWidth_;
if (lastEnd != addr.offset_)
EmitZero(ObjectAddr(lastEnd), addr.offset_ - lastEnd);
VisitExpr(init.expr_);
if (init.type_->IsScalar()) {
EmitStore(addr, init.type_);
} else if (init.type_->ToStruct()) {
CopyStruct(addr, init.type_->Width());
} else {
assert(false);
}
lastEnd = addr.offset_ + init.type_->Width();
}
auto objEnd = obj->Offset() + obj->Type()->Width();
if (lastEnd != objEnd)
EmitZero(ObjectAddr(lastEnd), objEnd - lastEnd);
return;
}
if (obj->Linkage() == L_NONE)
staticDecls_.push_back(decl);
else
GenStaticDecl(decl);
}
void Generator::GenStaticDecl(Declaration* decl) {
auto obj = decl->obj_;
assert(obj->IsStatic());
const auto& label = obj->Repr();
const auto width = obj->Type()->Width();
const auto align = obj->Align();
// Omit the external without initilizer
if ((obj->Storage() & S_EXTERN) && !obj->HasInit())
return;
Emit(".data");
auto glb = obj->Linkage() == L_EXTERNAL ? ".globl": ".local";
Emit(glb, label);
if (!obj->HasInit()) {
Emit(".comm", label + ", " + std::to_string(width) +
", " + std::to_string(align));
return;
}
Emit(".align", std::to_string(align));
Emit(".type", label, "@object");
// Does not decide the size of obj
Emit(".size", label, std::to_string(width));
EmitLabel(label);
int offset = 0;
auto iter = decl->Inits().begin();
for (; iter != decl->Inits().end();) {
auto staticInit = GetStaticInit(iter,
decl->Inits().end(), std::max(iter->offset_, offset));
if (staticInit.offset_ > offset)
Emit(".zero", std::to_string(staticInit.offset_ - offset));
switch (staticInit.width_) {
case 1:
Emit(".byte", std::to_string(static_cast<char>(staticInit.val_)));
break;
case 2:
Emit(".value", std::to_string(static_cast<short>(staticInit.val_)));
break;
case 4:
Emit(".long", std::to_string(static_cast<int>(staticInit.val_)));
break;
case 8: {
std::string val;
if (staticInit.label_.size() == 0) {
val = std::to_string(staticInit.val_);
} else if (staticInit.val_ != 0) {
val = staticInit.label_ + "+" + std::to_string(staticInit.val_);
} else {
val = staticInit.label_;
}
Emit(".quad", val);
} break;
default: assert(false);
}
offset = staticInit.offset_ + staticInit.width_;
}
// Decides the size of object
if (width > offset)
Emit(".zero", std::to_string(width - offset));
}
void Generator::VisitEmptyStmt(EmptyStmt* emptyStmt) {
assert(false);
}
void Generator::VisitIfStmt(IfStmt* ifStmt) {
VisitExpr(ifStmt->cond_);
// Compare to 0
auto elseLabel = LabelStmt::New();
auto endLabel = LabelStmt::New();
GenCompZero(ifStmt->cond_->Type());
if (ifStmt->else_) {
Emit("je", elseLabel);
} else {
Emit("je", endLabel);
}
VisitStmt(ifStmt->then_);
if (ifStmt->else_) {
Emit("jmp", endLabel);
EmitLabel(elseLabel->Repr());
VisitStmt(ifStmt->else_);
}
EmitLabel(endLabel->Repr());
}
void Generator::VisitJumpStmt(JumpStmt* jumpStmt) {
Emit("jmp", jumpStmt->label_);
}
void Generator::VisitLabelStmt(LabelStmt* labelStmt) {
EmitLabel(labelStmt->Repr());
}
void Generator::VisitReturnStmt(ReturnStmt* returnStmt) {
auto expr = returnStmt->expr_;
if (expr) { // The return expr could be nil
Visit(expr);
if (expr->Type()->ToStruct()) {
// %rax now has the address of the struct/union
ObjectAddr addr = ObjectAddr(retAddrOffset_);
Emit("movq", addr, "%r11");
addr = {"", "%r11", 0};
CopyStruct(addr, expr->Type()->Width());
Emit("movq", "%r11", "%rax");
}
}
Emit("jmp", curFunc_->retLabel_);
}
class Comp {
public:
bool operator()(Object* lhs, Object* rhs) {
return lhs->Align() < rhs->Align();
}
};
void Generator::AllocObjects(Scope* scope, const FuncDef::ParamList& params) {
int offset = offset_;
auto paramSet = std::set<Object*>(params.begin(), params.end());
std::priority_queue<Object*, std::vector<Object*>, Comp> heap;
for (auto iter = scope->begin(); iter != scope->end(); ++iter) {
auto obj = iter->second->ToObject();
if (!obj || obj->IsStatic())
continue;
if (paramSet.find(obj) != paramSet.end())
continue;
heap.push(obj);
}
while (!heap.empty()) {
auto obj = heap.top();
heap.pop();
offset -= obj->Type()->Width();
auto align = obj->Align();
if (obj->Type()->ToArray()) {
// The alignment of an array is at least the aligment of a pointer
// (as it is always cast to a pointer)
align = std::min(align, 8);
}
offset = Type::MakeAlign(offset, align);
obj->SetOffset(offset);
}
offset_ = offset;
}
void Generator::VisitCompoundStmt(CompoundStmt* compStmt) {
if (compStmt->scope_) {
AllocObjects(compStmt->scope_);
}
for (auto stmt: compStmt->stmts_) {
Visit(stmt);
}
}
void Generator::GetParamRegOffsets(int& gpOffset,
int& fpOffset,
int& overflow,
FuncType* funcType) {
TypeList types;
for (auto param: funcType->Params())
types.push_back(param->Type());
auto locations = GetParamLocations(types, funcType->Derived());
gpOffset = 0;
fpOffset = 48;
overflow = 16;
for (const auto& loc: locations.locs_) {
if (loc[1] == 'x')
fpOffset += 16;
else if (loc[1] == 'm')
overflow += 8;
else
gpOffset += 8;
}
}
void Generator::GenBuiltin(FuncCall* funcCall) {
struct va_list_imp {
unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
};
auto ap = UnaryOp::New(Token::DEREF, funcCall->args_[0]);
auto addr = LValGenerator().GenExpr(ap);
auto type = funcCall->FuncType();
auto offset = offsetof(va_list_imp, reg_save_area);
addr.offset_ += offset;
const auto& saveAreaAddr = addr.Repr();
addr.offset_ -= offset;
offset = offsetof(va_list_imp, overflow_arg_area);
addr.offset_ += offset;
const auto& overflowAddr = addr.Repr();
addr.offset_ -= offset;
offset = offsetof(va_list_imp, gp_offset);
addr.offset_ += offset;
const auto& gpOffsetAddr = addr.Repr();
addr.offset_ -= offset;
offset = offsetof(va_list_imp, fp_offset);
addr.offset_ += offset;
const auto& fpOffsetAddr = addr.Repr();
addr.offset_ -= offset;
if (type == Parser::vaStartType_) {
Emit("leaq", "-176(%rbp)", "%rax");
Emit("movq", "%rax", saveAreaAddr);
int gpOffset, fpOffset, overflowOffset;
GetParamRegOffsets(gpOffset, fpOffset,
overflowOffset, curFunc_->FuncType());
Emit("leaq", ObjectAddr(overflowOffset), "%rax");
Emit("movq", "%rax", overflowAddr);
Emit("movl", gpOffset, "%eax");
Emit("movl", "%eax", gpOffsetAddr);
Emit("movl", fpOffset, "%eax");
Emit("movl", "%eax", fpOffsetAddr);
} else if (type == Parser::vaArgType_) {
static int cnt[2] = {0, 0};
auto overflowLabel = ".L_va_arg_overflow" + std::to_string(++cnt[0]);
auto endLabel = ".L_va_arg_end" + std::to_string(++cnt[1]);
auto argType = funcCall->args_[1]->Type()->ToPointer()->Derived();
auto cls = Classify(argType.GetPtr());
if (cls == ParamClass::INTEGER) {
Emit("movq", saveAreaAddr, "%rax");
Emit("movq", "%rax", "%r11");
Emit("movl", gpOffsetAddr, "%eax");
Emit("cltq");
Emit("cmpq", 48, "%rax");
Emit("jae", overflowLabel);
Emit("addq", "%rax", "%r11");
Emit("addq", 8, "%rax");
Emit("movl", "%eax", gpOffsetAddr);
Emit("movq", "%r11", "%rax");
Emit("jmp", endLabel);
} else if (cls == ParamClass::SSE) {
Emit("movq", saveAreaAddr, "%rax");
Emit("movq", "%rax", "%r11");
Emit("movl", fpOffsetAddr, "%eax");
Emit("cltq");
Emit("cmpq", 176, "%rax");
Emit("jae", overflowLabel);
Emit("addq", "%rax", "%r11");
Emit("addq", 16, "%rax");
Emit("movl", "%eax", fpOffsetAddr);
Emit("movq", "%r11", "%rax");
Emit("jmp", endLabel);
} else if (cls == ParamClass::MEMORY) {
} else {
Error("internal error");
}
EmitLabel(overflowLabel);
Emit("movq", overflowAddr, "%rax");
Emit("movq", "%rax", "%r11");
// Arguments passed by memory is aligned by at least 8 bytes
Emit("addq", Type::MakeAlign(argType->Width(), 8), "%r11");
Emit("movq", "%r11", overflowAddr);
EmitLabel(endLabel);
} else {
assert(false);
}
}
void Generator::VisitFuncCall(FuncCall* funcCall) {
EmitLoc(funcCall);
auto funcType = funcCall->FuncType();
if (Parser::IsBuiltin(funcType))
return GenBuiltin(funcCall);
auto base = offset_;
// Alloc memory for return value if it is struct/union
int retStructOffset;
auto retType = funcCall->Type()->ToStruct();
if (retType) {
retStructOffset = offset_;
retStructOffset -= retType->Width();
retStructOffset = Type::MakeAlign(retStructOffset, retType->Align());
// No!!! you can't suppose that the
// visition of arguments won't change the value of %rdi
//Emit("leaq %d(#rbp), #rdi", offset);
offset_ = retStructOffset;
}
TypeList types;
for (auto arg: funcCall->args_) {
types.push_back(arg->Type());
}
const auto& locations = GetParamLocations(types, retType);
// Align stack frame by 16 bytes
const auto& locs = locations.locs_;
auto byMemCnt = locs.size() - locations.regCnt_ - locations.xregCnt_;
offset_ = Type::MakeAlign(offset_ - byMemCnt * 8, 16) + byMemCnt * 8;
for (int i = locs.size() - 1; i >=0; --i) {
if (locs[i][1] == 'm') {
Visit(funcCall->args_[i]);
Push(funcCall->args_[i]->Type());
}
}
for (int i = locs.size() - 1; i >= 0; --i) {
if (locs[i][1] == 'm')
continue;
Visit(funcCall->args_[i]);
Push(funcCall->args_[i]->Type());
}
for (const auto& loc: locs) {
if (loc[1] != 'm')
Pop(loc);
}
// If variadic, set %al to floating param number
if (funcType->Variadic()) {
Emit("movq", locations.xregCnt_, "%rax");
}
if (retType) {
Emit("leaq", ObjectAddr(retStructOffset), "%rdi");
}
Emit("leaq", ObjectAddr(offset_), "%rsp");
auto addr = LValGenerator().GenExpr(funcCall->Designator());
if (addr.base_.size() == 0 && addr.offset_ == 0) {
Emit("call", addr.label_);
} else {
Emit("leaq", addr, "%r10");
Emit("call", "*%r10");
}
// Reset stack frame
offset_ = base;
}
ParamLocations Generator::GetParamLocations(const TypeList& types,
bool retStruct) {
ParamLocations locations;
locations.regCnt_ = retStruct;
locations.xregCnt_ = 0;
for (auto type: types) {
auto cls = Classify(type);
const char* reg = nullptr;
if (cls == ParamClass::INTEGER) {
if (locations.regCnt_ < regs.size())
reg = regs[locations.regCnt_++];
} else if (cls == ParamClass::SSE) {
if (locations.xregCnt_ < xregs.size())
reg = xregs[locations.xregCnt_++];
}
locations.locs_.push_back(reg ? reg: "%mem");
}
return locations;
}
void Generator::VisitFuncDef(FuncDef* funcDef) {
curFunc_ = funcDef;
auto name = funcDef->Name();
Emit(".text");
if (funcDef->Linkage() == L_INTERNAL) {
Emit(".local", name);
} else {
Emit(".globl", name);
}
Emit(".type", name, "@function");
EmitLabel(name);
Emit("pushq", "%rbp");
Emit("movq", "%rsp", "%rbp");
offset_ = 0;
auto& params = funcDef->FuncType()->Params();
// Arrange space to store params passed by registers
bool retStruct = funcDef->FuncType()->Derived()->ToStruct();
TypeList types;
for (auto param: params)
types.push_back(param->Type());
auto locations = GetParamLocations(types, retStruct);
const auto& locs = locations.locs_;
if (funcDef->FuncType()->Variadic()) {
GenSaveArea(); // 'offset' is now the begin of save area
if (retStruct) {
retAddrOffset_ = offset_;
offset_ += 8;
}
int regOffset = offset_;
int xregOffset = offset_ + 48;
int byMemOffset = 16;
for (size_t i = 0; i < locs.size(); ++i) {
if (locs[i][1] == 'm') {
params[i]->SetOffset(byMemOffset);
// TODO(wgtdkp): width of incomplete array ?
// What about the var args, var args offset always increment by 8
//byMemOffset += 8;
byMemOffset += params[i]->Type()->Width();
byMemOffset = Type::MakeAlign(byMemOffset, 8);
} else if (locs[i][1] == 'x') {
params[i]->SetOffset(xregOffset);
xregOffset += 16;
} else {
params[i]->SetOffset(regOffset);
regOffset += 8;
}
}
} else {
if (retStruct) {
retAddrOffset_ = Push("%rdi");
}
int byMemOffset = 16;
for (size_t i = 0; i < locs.size(); ++i) {
if (locs[i][1] == 'm') {
params[i]->SetOffset(byMemOffset);
// TODO(wgtdkp): width of incomplete array ?
byMemOffset += params[i]->Type()->Width();
byMemOffset = Type::MakeAlign(byMemOffset, 8);
continue;
}
params[i]->SetOffset(Push(locs[i]));
}
}
AllocObjects(funcDef->Body()->Scope(), params);
for (auto stmt: funcDef->body_->stmts_) {
Visit(stmt);
}
EmitLabel(funcDef->retLabel_->Repr());
Emit("leaveq");
Emit("retq");
}
void Generator::GenSaveArea() {
static const int begin = -176;
int offset = begin;
for (auto reg: regs) {
Emit("movq", reg, ObjectAddr(offset));
offset += 8;
}
Emit("testb", "%al", "%al");
auto label = LabelStmt::New();
Emit("je", label);
for (auto xreg: xregs) {
Emit("movaps", xreg, ObjectAddr(offset));
offset += 16;
}
assert(offset == 0);
EmitLabel(label->Repr());
offset_ = begin;
}
void Generator::VisitTranslationUnit(TranslationUnit* unit) {
for (auto extDecl: unit->ExtDecls()) {
Visit(extDecl);
// Float and string literal
if (rodatas_.size())
Emit(".section", ".rodata");
for (auto rodata: rodatas_) {
if (rodata.align_ == 1) { // Literal
EmitLabel(rodata.label_);
Emit(".string", "\"" + rodata.sval_ + "\"");
} else if (rodata.align_ == 4) {
Emit(".align", "4");
EmitLabel(rodata.label_);
Emit(".long", std::to_string(static_cast<int>(rodata.ival_)));
} else {
Emit(".align", "8");
EmitLabel(rodata.label_);
Emit(".quad", std::to_string(rodata.ival_));
}
}
rodatas_.clear();
for (auto staticDecl: staticDecls_) {
GenStaticDecl(staticDecl);
}
staticDecls_.clear();
}
}
void Generator::Gen() {
Emit(".file", "\"" + filename_in + "\"");
VisitTranslationUnit(parser_->Unit());
}
void Generator::EmitLoc(Expr* expr) {
if (!debug) {
return;
}
static int fileno = 0;
if (expr->tok_ == nullptr) {
return;
}
const auto loc = &expr->tok_->loc_;
if (loc->filename_ != last_file) {
Emit(".file", std::to_string(++fileno) + " \"" + *loc->filename_ + "\"");
last_file = loc->filename_;
}
Emit(".loc", std::to_string(fileno) + " " +
std::to_string(loc->line_) + " 0");
std::string line;
for (const char* p = loc->lineBegin_; *p && *p != '\n'; ++p)
line.push_back(*p);
Emit("# " + line);
}
void Generator::EmitLoad(const std::string& addr, Type* type) {
assert(type->IsScalar());
EmitLoad(addr, type->Width(), type->IsFloat());
}
void Generator::EmitLoad(const std::string& addr, int width, bool flt) {
auto load = GetLoad(width, flt);
auto des = GetDes(width == 4 ? 4: 8, flt);
Emit(load, addr, des);
}
void Generator::EmitStore(const ObjectAddr& addr, Type* type) {
if (addr.bitFieldWidth_ != 0) {
EmitStoreBitField(addr, type);
} else {
EmitStore(addr.Repr(), type);
}
}
void Generator::EmitStore(const std::string& addr, Type* type) {
EmitStore(addr, type->Width(), type->IsFloat());
}
void Generator::EmitStore(const std::string& addr, int width, bool flt) {
auto store = GetInst("mov", width, flt);
auto des = GetDes(width, flt);
Emit(store, des, addr);
}
void Generator::EmitLabel(const std::string& label) {
fprintf(outFile_, "%s:\n", label.c_str());
}
void Generator::EmitZero(ObjectAddr addr, int width) {
int units[] = {8, 4, 2, 1};
Emit("xorq", "%rax", "%rax");
for (auto unit: units) {
while (width >= unit) {
EmitStore(addr.Repr(), unit, false);
addr.offset_ += unit;
width -= unit;
}
}
}
void LValGenerator::VisitBinaryOp(BinaryOp* binary) {
EmitLoc(binary);
assert(binary->op_ == '.');
addr_ = LValGenerator().GenExpr(binary->lhs_);
const auto& name = binary->rhs_->Tok()->str_;
auto structType = binary->lhs_->Type()->ToStruct();
auto member = structType->GetMember(name);
addr_.offset_ += member->Offset();
addr_.bitFieldBegin_ = member->bitFieldBegin_;
addr_.bitFieldWidth_ = member->bitFieldWidth_;
}
void LValGenerator::VisitUnaryOp(UnaryOp* unary) {
EmitLoc(unary);
assert(unary->op_ == Token::DEREF);
Generator().VisitExpr(unary->operand_);
Emit("movq", "%rax", "%r10");
addr_ = {"", "%r10", 0};
}
void LValGenerator::VisitObject(Object* obj) {
EmitLoc(obj);
if (!obj->IsStatic() && obj->Anonymous()) {
assert(obj->Decl());
Generator().Visit(obj->Decl());
obj->SetDecl(nullptr);
}
if (obj->IsStatic()) {
addr_ = {obj->Repr(), "%rip", 0};
} else {
addr_ = {"", "%rbp", obj->Offset()};
}
}
// The identifier must be function
void LValGenerator::VisitIdentifier(Identifier* ident) {
assert(!ident->ToTypeName());
EmitLoc(ident);
// Function address
addr_ = {ident->Name(), "", 0};
}
void LValGenerator::VisitTempVar(TempVar* tempVar) {
std::string label;
switch (tempVar->Type()->Width()) {
case 1: label = "%cl"; break;
case 2: label = "%cx"; break;
case 4: label = "%ecx"; break;
case 8: label = "%rcx"; break;
default: assert(false);
}
addr_ = {label, "", 0};
}
std::string ObjectAddr::Repr() const {
auto ret = base_.size() ? "(" + base_ + ")": "";
if (label_.size() == 0) {
if (offset_ == 0) {
return ret;
}
return std::to_string(offset_) + ret;
} else {
if (offset_ == 0) {
return label_ + ret;
}
return label_ + "+" + std::to_string(offset_) + ret;
}
}
StaticInitializer Generator::GetStaticInit(InitList::iterator& iter,
InitList::iterator end,
int offset) {
auto init = iter++;
auto width = init->type_->Width();
if (init->type_->IsInteger()) {
if (init->bitFieldWidth_ == 0) {
auto val = Evaluator<long>().Eval(init->expr_);
return {init->offset_, width, val, ""};
}
int totalBits = 0;
unsigned char val = 0;
while (init != end && init->offset_ <= offset && totalBits < 8) {
auto bitVal = Evaluator<long>().Eval(init->expr_);
auto begin = init->bitFieldBegin_;
auto width = init->bitFieldWidth_;
auto valBegin = 0;
auto valWidth = 0;
auto mask = 0UL;
if (init->offset_ < offset) {
begin = 0;
width -= (8 - init->bitFieldBegin_);
if (offset - init->offset_ > 1)
width -= (offset - init->offset_ - 1) * 8;
valBegin = init->bitFieldWidth_ - width;
}
valWidth = std::min(static_cast<unsigned char>(8 - begin), width);
mask = Object::BitFieldMask(valBegin, valWidth);
val |= ((bitVal & mask) >> valBegin) << begin;
totalBits = begin + valWidth;
if (width - valWidth <= 0)
++init;
}
iter = init;
return {offset, 1, val, ""};
} else if (init->type_->IsFloat()) {
auto val = Evaluator<double>().Eval(init->expr_);
auto lval = *reinterpret_cast<long*>(&val);
return {init->offset_, width, lval, ""};
} else if (init->type_->ToPointer()) {
auto addr = Evaluator<Addr>().Eval(init->expr_);
return {init->offset_, width, addr.offset_, addr.label_};
} else { // Struct initializer
Error(init->expr_, "initializer element is not constant");
return StaticInitializer(); // Make compiler happy
}
}