mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-03-03 11:03:14 +01:00
[svn r235] rough port of GDC's inline assembler code, unfinished
This commit is contained in:
@@ -320,8 +320,8 @@ int main(int argc, char *argv[])
|
||||
#endif /* linux */
|
||||
|
||||
//VersionCondition::addPredefinedGlobalIdent("D_Bits");
|
||||
//VersionCondition::addPredefinedGlobalIdent("D_InlineAsm");
|
||||
//VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86");
|
||||
VersionCondition::addPredefinedGlobalIdent("D_InlineAsm");
|
||||
VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86");
|
||||
VersionCondition::addPredefinedGlobalIdent("all");
|
||||
|
||||
#if _WIN32
|
||||
|
||||
@@ -761,9 +761,8 @@ struct LabelStatement : Statement
|
||||
struct LabelDsymbol : Dsymbol
|
||||
{
|
||||
LabelStatement *statement;
|
||||
#if IN_GCC
|
||||
unsigned asmLabelNum; // GCC-specific
|
||||
#endif
|
||||
// LLVMDC
|
||||
unsigned asmLabelNum; // for inline assembler labels
|
||||
|
||||
LabelDsymbol(Identifier *ident);
|
||||
LabelDsymbol *isLabel();
|
||||
|
||||
418
gen/asmstmt.cpp
Normal file
418
gen/asmstmt.cpp
Normal file
@@ -0,0 +1,418 @@
|
||||
// Taken from GDC source tree, licence unclear?
|
||||
//
|
||||
// Taken from an earlier version of DMD -- why is it missing from 0.79?
|
||||
|
||||
//#include "d-gcc-includes.h"
|
||||
//#include "total.h"
|
||||
#include "dmd/statement.h"
|
||||
#include "dmd/scope.h"
|
||||
#include "dmd/declaration.h"
|
||||
#include "dmd/dsymbol.h"
|
||||
|
||||
#include "llvm/InlineAsm.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
//#include "d-lang.h"
|
||||
//#include "d-codegen.h"
|
||||
|
||||
typedef enum {
|
||||
Arg_Integer,
|
||||
Arg_Pointer,
|
||||
Arg_Memory,
|
||||
Arg_FrameRelative,
|
||||
Arg_LocalSize,
|
||||
Arg_Dollar
|
||||
} AsmArgType;
|
||||
|
||||
typedef enum {
|
||||
Mode_Input,
|
||||
Mode_Output,
|
||||
Mode_Update
|
||||
} AsmArgMode;
|
||||
|
||||
struct AsmArg {
|
||||
AsmArgType type;
|
||||
Expression * expr;
|
||||
AsmArgMode mode;
|
||||
AsmArg(AsmArgType type, Expression * expr, AsmArgMode mode) {
|
||||
this->type = type;
|
||||
this->expr = expr;
|
||||
this->mode = mode;
|
||||
}
|
||||
};
|
||||
|
||||
struct AsmCode {
|
||||
char * insnTemplate;
|
||||
unsigned insnTemplateLen;
|
||||
Array args; // of AsmArg
|
||||
unsigned moreRegs;
|
||||
unsigned dollarLabel;
|
||||
int clobbersMemory;
|
||||
AsmCode() {
|
||||
insnTemplate = NULL;
|
||||
insnTemplateLen = 0;
|
||||
moreRegs = 0;
|
||||
dollarLabel = 0;
|
||||
clobbersMemory = 0;
|
||||
}
|
||||
};
|
||||
|
||||
llvm::InlineAsm*
|
||||
d_build_asm_stmt(std::string code, std::deque<DValue*> const& output_values, std::deque<DValue*> const& input_values, std::string constraints)
|
||||
{
|
||||
//FIXME: Return InlineAsm::get(..) here.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AsmStatement::AsmStatement(Loc loc, Token *tokens) :
|
||||
Statement(loc)
|
||||
{
|
||||
this->tokens = tokens; // Do I need to copy these?
|
||||
asmcode = 0;
|
||||
asmalign = 0;
|
||||
refparam = 0;
|
||||
naked = 0;
|
||||
regs = 0;
|
||||
}
|
||||
|
||||
Statement *AsmStatement::syntaxCopy()
|
||||
{
|
||||
// copy tokens? copy 'code'?
|
||||
AsmStatement * a_s = new AsmStatement(loc,tokens);
|
||||
a_s->asmcode = asmcode;
|
||||
a_s->refparam = refparam;
|
||||
a_s->naked = naked;
|
||||
a_s->regs = a_s->regs;
|
||||
return a_s;
|
||||
}
|
||||
|
||||
void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
||||
{
|
||||
bool sep = 0, nsep = 0;
|
||||
buf->writestring("asm { ");
|
||||
|
||||
for (Token * t = tokens; t; t = t->next) {
|
||||
switch (t->value) {
|
||||
case TOKlparen:
|
||||
case TOKrparen:
|
||||
case TOKlbracket:
|
||||
case TOKrbracket:
|
||||
case TOKcolon:
|
||||
case TOKsemicolon:
|
||||
case TOKcomma:
|
||||
case TOKstring:
|
||||
case TOKcharv:
|
||||
case TOKwcharv:
|
||||
case TOKdcharv:
|
||||
nsep = 0;
|
||||
break;
|
||||
default:
|
||||
nsep = 1;
|
||||
}
|
||||
if (sep + nsep == 2)
|
||||
buf->writeByte(' ');
|
||||
sep = nsep;
|
||||
buf->writestring(t->toChars());
|
||||
}
|
||||
buf->writestring("; }");
|
||||
buf->writenl();
|
||||
}
|
||||
|
||||
int AsmStatement::comeFrom()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* GCC does not support jumps from asm statements. When optimization
|
||||
is turned on, labels referenced only from asm statements will not
|
||||
be output at the correct location. There are ways around this:
|
||||
|
||||
1) Reference the label with a reachable goto statement
|
||||
2) Have reachable computed goto in the function
|
||||
3) Hack cfgbuild.c to act as though there is a computed goto.
|
||||
|
||||
These are all pretty bad, but if would be nice to be able to tell
|
||||
GCC not to optimize in this case (even on per label/block basis).
|
||||
|
||||
The current solution is output our own private labels (as asm
|
||||
statements) along with the "real" label. If the label happens to
|
||||
be referred to by a goto statement, the "real" label will also be
|
||||
output in the correct location.
|
||||
|
||||
Also had to add 'asmLabelNum' to LabelDsymbol to indicate it needs
|
||||
special processing.
|
||||
|
||||
(junk) d-lang.cc:916:case LABEL_DECL: // C doesn't do this. D needs this for referencing labels in inline assembler since there may be not goto referencing it.
|
||||
|
||||
*/
|
||||
|
||||
static unsigned d_priv_asm_label_serial = 0;
|
||||
|
||||
// may need to make this target-specific
|
||||
static void d_format_priv_asm_label(char * buf, unsigned n)
|
||||
{
|
||||
//ASM_GENERATE_INTERNAL_LABEL(buf, "LDASM", n);//inserts a '*' for use with assemble_name
|
||||
sprintf(buf, ".LDASM%u", n);
|
||||
}
|
||||
|
||||
void
|
||||
d_expand_priv_asm_label(IRState * irs, unsigned n)
|
||||
{
|
||||
/* char buf[64];
|
||||
d_format_priv_asm_label(buf, n);
|
||||
strcat(buf, ":");
|
||||
tree insnt = build_string(strlen(buf), buf);
|
||||
#if D_GCC_VER < 40
|
||||
expand_asm(insnt, 1);
|
||||
#else
|
||||
tree t = d_build_asm_stmt(insnt, NULL_TREE, NULL_TREE, NULL_TREE);
|
||||
ASM_VOLATILE_P( t ) = 1;
|
||||
ASM_INPUT_P( t) = 1; // what is this doing?
|
||||
irs->addExp(t);
|
||||
#endif*/
|
||||
}
|
||||
|
||||
|
||||
// StringExp::toIR usually adds a NULL. We don't want that...
|
||||
|
||||
/*static tree
|
||||
naturalString(Expression * e)
|
||||
{
|
||||
// don't fail, just an error?
|
||||
assert(e->op == TOKstring);
|
||||
StringExp * s = (StringExp *) e;
|
||||
assert(s->sz == 1);
|
||||
return build_string(s->len, (char *) s->string);
|
||||
}*/
|
||||
|
||||
|
||||
#include "d-asm-i386.h"
|
||||
|
||||
bool d_have_inline_asm() { return true; }
|
||||
|
||||
Statement *AsmStatement::semantic(Scope *sc)
|
||||
{
|
||||
|
||||
sc->func->inlineAsm = 1;
|
||||
sc->func->inlineStatus = ILSno; // %% not sure
|
||||
// %% need to set DECL_UNINLINABLE too?
|
||||
sc->func->hasReturnExp = 1; // %% DMD does this, apparently...
|
||||
|
||||
// empty statement -- still do the above things because they might be expected?
|
||||
if (! tokens)
|
||||
return this;
|
||||
|
||||
AsmProcessor ap(sc, this);
|
||||
ap.run();
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
AsmStatement::toIR(IRState * irs)
|
||||
{
|
||||
// FIXME
|
||||
// gen.doLineNote( loc );
|
||||
|
||||
if (! asmcode)
|
||||
return;
|
||||
|
||||
static std::string i_cns = "i";
|
||||
static std::string p_cns = "p";
|
||||
static std::string m_cns = "m";
|
||||
static std::string mw_cns = "=m";
|
||||
static std::string mrw_cns = "+m";
|
||||
static std::string memory_name = "memory";
|
||||
|
||||
AsmCode * code = (AsmCode *) asmcode;
|
||||
std::deque<DValue*> input_values;
|
||||
std::deque<std::string> input_constraints;
|
||||
std::deque<DValue*> output_values;
|
||||
std::deque<std::string> output_constraints;
|
||||
std::deque<std::string> clobbers;
|
||||
|
||||
// FIXME
|
||||
#define HOST_WIDE_INT long
|
||||
HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro
|
||||
bool clobbers_mem = code->clobbersMemory;
|
||||
int input_idx = 0;
|
||||
int n_outputs = 0;
|
||||
int arg_map[10];
|
||||
|
||||
assert(code->args.dim <= 10);
|
||||
|
||||
for (unsigned i = 0; i < code->args.dim; i++) {
|
||||
AsmArg * arg = (AsmArg *) code->args.data[i];
|
||||
|
||||
bool is_input = true;
|
||||
DValue* arg_val;
|
||||
std::string cns;
|
||||
|
||||
switch (arg->type) {
|
||||
case Arg_Integer:
|
||||
arg_val = arg->expr->toElem(irs);
|
||||
do_integer:
|
||||
cns = i_cns;
|
||||
break;
|
||||
case Arg_Pointer:
|
||||
// FIXME
|
||||
/* if (arg->expr->op == TOKvar)
|
||||
arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree;
|
||||
else if (arg->expr->op == TOKdsymbol) {
|
||||
arg_val = irs->getLabelTree( (LabelDsymbol *) ((DsymbolExp *) arg->expr)->s );
|
||||
} else
|
||||
assert(0);
|
||||
arg_val = irs->addressOf(arg_val);*/
|
||||
cns = p_cns;
|
||||
break;
|
||||
case Arg_Memory:
|
||||
// FIXME
|
||||
/* if (arg->expr->op == TOKvar)
|
||||
arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree;
|
||||
else
|
||||
arg_val = arg->expr->toElem(irs);
|
||||
if (DECL_P( arg_val ))
|
||||
TREE_ADDRESSABLE( arg_val ) = 1;*/
|
||||
switch (arg->mode) {
|
||||
case Mode_Input: cns = m_cns; break;
|
||||
case Mode_Output: cns = mw_cns; is_input = false; break;
|
||||
case Mode_Update: cns = mrw_cns; is_input = false; break;
|
||||
default: assert(0); break;
|
||||
}
|
||||
break;
|
||||
case Arg_FrameRelative:
|
||||
// FIXME
|
||||
/* if (arg->expr->op == TOKvar)
|
||||
arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree;
|
||||
else
|
||||
assert(0);*/
|
||||
if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) {
|
||||
// arg_val = irs->integerConstant(var_frame_offset);
|
||||
cns = i_cns;
|
||||
} else {
|
||||
this->error("%s", "argument not frame relative");
|
||||
return;
|
||||
}
|
||||
if (arg->mode != Mode_Input)
|
||||
clobbers_mem = true;
|
||||
break;
|
||||
case Arg_LocalSize:
|
||||
// FIXME
|
||||
/* var_frame_offset = cfun->x_frame_offset;
|
||||
if (var_frame_offset < 0)
|
||||
var_frame_offset = - var_frame_offset;
|
||||
arg_val = irs->integerConstant( var_frame_offset );*/
|
||||
goto do_integer;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (is_input) {
|
||||
arg_map[i] = --input_idx;
|
||||
//inputs.cons(tree_cons(NULL_TREE, cns, NULL_TREE), arg_val);
|
||||
input_values.push_back(arg_val);
|
||||
input_constraints.push_back(cns);
|
||||
} else {
|
||||
arg_map[i] = n_outputs++;
|
||||
//outputs.cons(tree_cons(NULL_TREE, cns, NULL_TREE), arg_val);
|
||||
output_values.push_back(arg_val);
|
||||
output_constraints.push_back(cns);
|
||||
}
|
||||
}
|
||||
|
||||
// Telling GCC that callee-saved registers are clobbered makes it preserve
|
||||
// those registers. This changes the stack from what a naked function
|
||||
// expects.
|
||||
|
||||
// FIXME
|
||||
// if (! irs->func->naked) {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (regs & (1 << i)) {
|
||||
//clobbers.cons(NULL_TREE, regInfo[i].gccName);
|
||||
clobbers.push_back(regInfo[i].gccName);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (code->moreRegs & (1 << (i-32))) {
|
||||
//clobbers.cons(NULL_TREE, regInfo[i].gccName);
|
||||
clobbers.push_back(regInfo[i].gccName);
|
||||
}
|
||||
}
|
||||
if (clobbers_mem)
|
||||
clobbers.push_back(memory_name);
|
||||
//clobbers.cons(NULL_TREE, memory_name);
|
||||
// }
|
||||
|
||||
|
||||
// Remap argument numbers
|
||||
for (unsigned i = 0; i < code->args.dim; i++) {
|
||||
if (arg_map[i] < 0)
|
||||
arg_map[i] = -arg_map[i] - 1 + n_outputs;
|
||||
}
|
||||
|
||||
bool pct = false;
|
||||
char * p = code->insnTemplate;
|
||||
char * q = p + code->insnTemplateLen;
|
||||
//printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate);
|
||||
while (p < q) {
|
||||
if (pct) {
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
// %% doesn't check against nargs
|
||||
*p = '0' + arg_map[*p - '0'];
|
||||
pct = false;
|
||||
} else if (*p == '$') {
|
||||
pct = false;
|
||||
}
|
||||
//assert(*p == '%');// could be 'a', etc. so forget it..
|
||||
} else if (*p == '$')
|
||||
pct = true;
|
||||
++p;
|
||||
}
|
||||
|
||||
//printf("final: %.*s\n", code->insnTemplateLen, code->insnTemplate);
|
||||
|
||||
std::string insnt(code->insnTemplate, code->insnTemplateLen);
|
||||
|
||||
// rewrite GCC-style constraints to LLVM-style constraints
|
||||
std::string llvmConstraints;
|
||||
int n = 0;
|
||||
typedef std::deque<std::string>::iterator it;
|
||||
for(it i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i, ++n) {
|
||||
// rewrite update constraint to in and out constraints
|
||||
if((*i)[0] == '+') {
|
||||
(*i)[0] = '=';
|
||||
std::string input_constraint;
|
||||
std::stringstream ss;
|
||||
ss << n;
|
||||
ss >> input_constraint;
|
||||
//FIXME: I think multiple inout constraints will mess up the order!
|
||||
input_constraints.push_front(input_constraint);
|
||||
input_values.push_front(output_values[n]);
|
||||
}
|
||||
llvmConstraints += *i;
|
||||
llvmConstraints += ",";
|
||||
}
|
||||
for(it i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) {
|
||||
llvmConstraints += *i;
|
||||
llvmConstraints += ",";
|
||||
}
|
||||
for(it i = clobbers.begin(), e = clobbers.end(); i != e; ++i) {
|
||||
llvmConstraints += "~{" + *i + "},";
|
||||
}
|
||||
llvmConstraints.resize(llvmConstraints.size()-1);
|
||||
|
||||
std::cout << "Inline Asm code: " << std::endl;
|
||||
std::cout << insnt << std::endl;
|
||||
std::cout << "LLVM constraints: " << llvmConstraints << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
llvm::InlineAsm* t = d_build_asm_stmt(insnt, output_values, input_values, llvmConstraints);
|
||||
// FIXME
|
||||
//ASM_VOLATILE_P( t ) = 1;
|
||||
//irs->addExp( t );
|
||||
if (code->dollarLabel)
|
||||
d_expand_priv_asm_label(irs, code->dollarLabel);
|
||||
}
|
||||
2658
gen/d-asm-i386.h
Normal file
2658
gen/d-asm-i386.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1111,12 +1111,13 @@ void SynchronizedStatement::toIR(IRState* p)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* this has moved to asmstmt.cpp
|
||||
void AsmStatement::toIR(IRState* p)
|
||||
{
|
||||
Logger::println("AsmStatement::toIR(): %s", loc.toChars());
|
||||
LOG_SCOPE;
|
||||
error("%s: inline asm is not yet implemented", loc.toChars());
|
||||
fatal();
|
||||
// error("%s: inline asm is not yet implemented", loc.toChars());
|
||||
// fatal();
|
||||
|
||||
assert(!asmcode && !asmalign && !refparam && !naked && !regs);
|
||||
|
||||
@@ -1142,7 +1143,7 @@ void AsmStatement::toIR(IRState* p)
|
||||
|
||||
assert(0);
|
||||
}
|
||||
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void VolatileStatement::toIR(IRState* p)
|
||||
|
||||
14
gen/toir.cpp
14
gen/toir.cpp
@@ -3148,6 +3148,7 @@ int TypedefDeclaration::cvMember(unsigned char*)
|
||||
|
||||
void obj_includelib(char*){}
|
||||
|
||||
/* this has moved to asmstmt.cpp
|
||||
AsmStatement::AsmStatement(Loc loc, Token *tokens) :
|
||||
Statement(loc)
|
||||
{
|
||||
@@ -3155,9 +3156,9 @@ AsmStatement::AsmStatement(Loc loc, Token *tokens) :
|
||||
}
|
||||
Statement *AsmStatement::syntaxCopy()
|
||||
{
|
||||
/*error("%s: inline asm is not yet implemented", loc.toChars());
|
||||
fatal();
|
||||
assert(0);*/
|
||||
//error("%s: inline asm is not yet implemented", loc.toChars());
|
||||
//fatal();
|
||||
//assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3174,11 +3175,12 @@ void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
|
||||
|
||||
int AsmStatement::comeFrom()
|
||||
{
|
||||
/*error("%s: inline asm is not yet implemented", loc.toChars());
|
||||
fatal();
|
||||
assert(0);*/
|
||||
//error("%s: inline asm is not yet implemented", loc.toChars());
|
||||
//fatal();
|
||||
//assert(0);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
void
|
||||
backend_init()
|
||||
|
||||
Reference in New Issue
Block a user