Mercurial > projects > ldc
diff gen/asmstmt.cpp @ 219:761c8352f494 trunk
[svn r235] rough port of GDC's inline assembler code, unfinished
author | ChristianK |
---|---|
date | Thu, 05 Jun 2008 19:22:37 +0200 |
parents | |
children | ccc2e6898a78 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen/asmstmt.cpp Thu Jun 05 19:22:37 2008 +0200 @@ -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); +}