| 1 |
// Taken from GDC source tree. Original by David Friedman. |
|---|
| 2 |
// Released under the Artistic License found in dmd/artistic.txt |
|---|
| 3 |
|
|---|
| 4 |
#include "gen/llvm.h" |
|---|
| 5 |
#include "llvm/InlineAsm.h" |
|---|
| 6 |
|
|---|
| 7 |
//#include "d-gcc-includes.h" |
|---|
| 8 |
//#include "total.h" |
|---|
| 9 |
#include "mars.h" |
|---|
| 10 |
#include "statement.h" |
|---|
| 11 |
#include "scope.h" |
|---|
| 12 |
#include "declaration.h" |
|---|
| 13 |
#include "dsymbol.h" |
|---|
| 14 |
|
|---|
| 15 |
#include <cassert> |
|---|
| 16 |
#include <deque> |
|---|
| 17 |
#include <cstring> |
|---|
| 18 |
#include <string> |
|---|
| 19 |
#include <sstream> |
|---|
| 20 |
|
|---|
| 21 |
//#include "d-lang.h" |
|---|
| 22 |
//#include "d-codegen.h" |
|---|
| 23 |
|
|---|
| 24 |
#include "gen/irstate.h" |
|---|
| 25 |
#include "gen/dvalue.h" |
|---|
| 26 |
#include "gen/tollvm.h" |
|---|
| 27 |
#include "gen/logger.h" |
|---|
| 28 |
#include "gen/todebug.h" |
|---|
| 29 |
#include "gen/llvmhelpers.h" |
|---|
| 30 |
#include "gen/functions.h" |
|---|
| 31 |
|
|---|
| 32 |
typedef enum { |
|---|
| 33 |
Arg_Integer, |
|---|
| 34 |
Arg_Pointer, |
|---|
| 35 |
Arg_Memory, |
|---|
| 36 |
Arg_FrameRelative, |
|---|
| 37 |
Arg_LocalSize, |
|---|
| 38 |
Arg_Dollar |
|---|
| 39 |
} AsmArgType; |
|---|
| 40 |
|
|---|
| 41 |
typedef enum { |
|---|
| 42 |
Mode_Input, |
|---|
| 43 |
Mode_Output, |
|---|
| 44 |
Mode_Update |
|---|
| 45 |
} AsmArgMode; |
|---|
| 46 |
|
|---|
| 47 |
struct AsmArg { |
|---|
| 48 |
Expression * expr; |
|---|
| 49 |
AsmArgType type; |
|---|
| 50 |
AsmArgMode mode; |
|---|
| 51 |
AsmArg(AsmArgType type, Expression * expr, AsmArgMode mode) { |
|---|
| 52 |
this->type = type; |
|---|
| 53 |
this->expr = expr; |
|---|
| 54 |
this->mode = mode; |
|---|
| 55 |
} |
|---|
| 56 |
}; |
|---|
| 57 |
|
|---|
| 58 |
struct AsmCode { |
|---|
| 59 |
std::string insnTemplate; |
|---|
| 60 |
std::vector<AsmArg> args; |
|---|
| 61 |
std::vector<bool> regs; |
|---|
| 62 |
unsigned dollarLabel; |
|---|
| 63 |
int clobbersMemory; |
|---|
| 64 |
AsmCode(int n_regs) { |
|---|
| 65 |
regs.resize(n_regs, false); |
|---|
| 66 |
dollarLabel = 0; |
|---|
| 67 |
clobbersMemory = 0; |
|---|
| 68 |
} |
|---|
| 69 |
}; |
|---|
| 70 |
|
|---|
| 71 |
AsmStatement::AsmStatement(Loc loc, Token *tokens) : |
|---|
| 72 |
Statement(loc) |
|---|
| 73 |
{ |
|---|
| 74 |
this->tokens = tokens; // Do I need to copy these? |
|---|
| 75 |
asmcode = 0; |
|---|
| 76 |
asmalign = 0; |
|---|
| 77 |
refparam = 0; |
|---|
| 78 |
naked = 0; |
|---|
| 79 |
|
|---|
| 80 |
isBranchToLabel = NULL; |
|---|
| 81 |
} |
|---|
| 82 |
|
|---|
| 83 |
Statement *AsmStatement::syntaxCopy() |
|---|
| 84 |
{ |
|---|
| 85 |
// copy tokens? copy 'code'? |
|---|
| 86 |
AsmStatement * a_s = new AsmStatement(loc,tokens); |
|---|
| 87 |
a_s->asmcode = asmcode; |
|---|
| 88 |
a_s->refparam = refparam; |
|---|
| 89 |
a_s->naked = naked; |
|---|
| 90 |
return a_s; |
|---|
| 91 |
} |
|---|
| 92 |
|
|---|
| 93 |
void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) |
|---|
| 94 |
{ |
|---|
| 95 |
bool sep = 0, nsep = 0; |
|---|
| 96 |
buf->writestring("asm { "); |
|---|
| 97 |
|
|---|
| 98 |
for (Token * t = tokens; t; t = t->next) { |
|---|
| 99 |
switch (t->value) { |
|---|
| 100 |
case TOKlparen: |
|---|
| 101 |
case TOKrparen: |
|---|
| 102 |
case TOKlbracket: |
|---|
| 103 |
case TOKrbracket: |
|---|
| 104 |
case TOKcolon: |
|---|
| 105 |
case TOKsemicolon: |
|---|
| 106 |
case TOKcomma: |
|---|
| 107 |
case TOKstring: |
|---|
| 108 |
case TOKcharv: |
|---|
| 109 |
case TOKwcharv: |
|---|
| 110 |
case TOKdcharv: |
|---|
| 111 |
nsep = 0; |
|---|
| 112 |
break; |
|---|
| 113 |
default: |
|---|
| 114 |
nsep = 1; |
|---|
| 115 |
} |
|---|
| 116 |
if (sep + nsep == 2) |
|---|
| 117 |
buf->writeByte(' '); |
|---|
| 118 |
sep = nsep; |
|---|
| 119 |
buf->writestring(t->toChars()); |
|---|
| 120 |
} |
|---|
| 121 |
buf->writestring("; }"); |
|---|
| 122 |
buf->writenl(); |
|---|
| 123 |
} |
|---|
| 124 |
|
|---|
| 125 |
int AsmStatement::comeFrom() |
|---|
| 126 |
{ |
|---|
| 127 |
return FALSE; |
|---|
| 128 |
} |
|---|
| 129 |
|
|---|
| 130 |
struct AsmParserCommon |
|---|
| 131 |
{ |
|---|
| 132 |
virtual void run(Scope* sc, AsmStatement* asmst) = 0; |
|---|
| 133 |
virtual std::string getRegName(int i) = 0; |
|---|
| 134 |
}; |
|---|
| 135 |
AsmParserCommon* asmparser = NULL; |
|---|
| 136 |
|
|---|
| 137 |
#include "asm-x86-32.h" |
|---|
| 138 |
#include "asm-x86-64.h" |
|---|
| 139 |
|
|---|
| 140 |
bool d_have_inline_asm() { return true; } |
|---|
| 141 |
|
|---|
| 142 |
Statement *AsmStatement::semantic(Scope *sc) |
|---|
| 143 |
{ |
|---|
| 144 |
bool err = false; |
|---|
| 145 |
if ((global.params.cpu != ARCHx86) && (global.params.cpu != ARCHx86_64)) |
|---|
| 146 |
{ |
|---|
| 147 |
error("inline asm is not supported for the \"%s\" architecture", global.params.llvmArch); |
|---|
| 148 |
err = true; |
|---|
| 149 |
} |
|---|
| 150 |
if (!global.params.useInlineAsm) |
|---|
| 151 |
{ |
|---|
| 152 |
error("inline asm is not allowed when the -noasm switch is used"); |
|---|
| 153 |
err = true; |
|---|
| 154 |
} |
|---|
| 155 |
if (err) |
|---|
| 156 |
fatal(); |
|---|
| 157 |
|
|---|
| 158 |
//puts(toChars()); |
|---|
| 159 |
|
|---|
| 160 |
sc->func->inlineAsm = 1; |
|---|
| 161 |
sc->func->inlineStatus = ILSno; // %% not sure |
|---|
| 162 |
// %% need to set DECL_UNINLINABLE too? |
|---|
| 163 |
sc->func->hasReturnExp = 1; // %% DMD does this, apparently... |
|---|
| 164 |
|
|---|
| 165 |
// empty statement -- still do the above things because they might be expected? |
|---|
| 166 |
if (! tokens) |
|---|
| 167 |
return this; |
|---|
| 168 |
|
|---|
| 169 |
if (!asmparser) |
|---|
| 170 |
if (global.params.cpu == ARCHx86) |
|---|
| 171 |
asmparser = new AsmParserx8632::AsmParser; |
|---|
| 172 |
else if (global.params.cpu == ARCHx86_64) |
|---|
| 173 |
asmparser = new AsmParserx8664::AsmParser; |
|---|
| 174 |
|
|---|
| 175 |
asmparser->run(sc, this); |
|---|
| 176 |
|
|---|
| 177 |
return this; |
|---|
| 178 |
} |
|---|
| 179 |
|
|---|
| 180 |
int AsmStatement::blockExit() |
|---|
| 181 |
{ |
|---|
| 182 |
//printf("AsmStatement::blockExit(%p)\n", this); |
|---|
| 183 |
return BEfallthru | BEreturn | BEgoto | BEhalt; |
|---|
| 184 |
} |
|---|
| 185 |
|
|---|
| 186 |
void |
|---|
| 187 |
AsmStatement::toIR(IRState * irs) |
|---|
| 188 |
{ |
|---|
| 189 |
Logger::println("AsmStatement::toIR(): %s", loc.toChars()); |
|---|
| 190 |
LOG_SCOPE; |
|---|
| 191 |
|
|---|
| 192 |
// sanity check |
|---|
| 193 |
assert(irs->func()->decl->inlineAsm); |
|---|
| 194 |
|
|---|
| 195 |
// get asm block |
|---|
| 196 |
IRAsmBlock* asmblock = irs->asmBlock; |
|---|
| 197 |
assert(asmblock); |
|---|
| 198 |
|
|---|
| 199 |
#ifndef DISABLE_DEBUG_INFO |
|---|
| 200 |
// debug info |
|---|
| 201 |
if (global.params.symdebug) |
|---|
| 202 |
DtoDwarfStopPoint(loc.linnum); |
|---|
| 203 |
#endif |
|---|
| 204 |
|
|---|
| 205 |
if (! asmcode) |
|---|
| 206 |
return; |
|---|
| 207 |
|
|---|
| 208 |
static std::string i_cns = "i"; |
|---|
| 209 |
static std::string p_cns = "i"; |
|---|
| 210 |
static std::string m_cns = "*m"; |
|---|
| 211 |
static std::string mw_cns = "=*m"; |
|---|
| 212 |
static std::string mrw_cns = "+*m"; |
|---|
| 213 |
static std::string memory_name = "memory"; |
|---|
| 214 |
|
|---|
| 215 |
AsmCode * code = (AsmCode *) asmcode; |
|---|
| 216 |
std::vector<LLValue*> input_values; |
|---|
| 217 |
std::vector<std::string> input_constraints; |
|---|
| 218 |
std::vector<LLValue*> output_values; |
|---|
| 219 |
std::vector<std::string> output_constraints; |
|---|
| 220 |
std::vector<std::string> clobbers; |
|---|
| 221 |
|
|---|
| 222 |
// FIXME |
|---|
| 223 |
//#define HOST_WIDE_INT long |
|---|
| 224 |
//HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro |
|---|
| 225 |
bool clobbers_mem = code->clobbersMemory; |
|---|
| 226 |
int input_idx = 0; |
|---|
| 227 |
int n_outputs = 0; |
|---|
| 228 |
int arg_map[10]; |
|---|
| 229 |
|
|---|
| 230 |
assert(code->args.size() <= 10); |
|---|
| 231 |
|
|---|
| 232 |
std::vector<AsmArg>::iterator arg = code->args.begin(); |
|---|
| 233 |
for (unsigned i = 0; i < code->args.size(); i++, ++arg) { |
|---|
| 234 |
bool is_input = true; |
|---|
| 235 |
LLValue* arg_val = 0; |
|---|
| 236 |
std::string cns; |
|---|
| 237 |
|
|---|
| 238 |
switch (arg->type) { |
|---|
| 239 |
case Arg_Integer: |
|---|
| 240 |
arg_val = arg->expr->toElem(irs)->getRVal(); |
|---|
| 241 |
do_integer: |
|---|
| 242 |
cns = i_cns; |
|---|
| 243 |
break; |
|---|
| 244 |
case Arg_Pointer: |
|---|
| 245 |
assert(arg->expr->op == TOKvar); |
|---|
| 246 |
arg_val = arg->expr->toElem(irs)->getRVal(); |
|---|
| 247 |
cns = p_cns; |
|---|
| 248 |
|
|---|
| 249 |
break; |
|---|
| 250 |
case Arg_Memory: |
|---|
| 251 |
arg_val = arg->expr->toElem(irs)->getRVal(); |
|---|
| 252 |
|
|---|
| 253 |
switch (arg->mode) { |
|---|
| 254 |
case Mode_Input: cns = m_cns; break; |
|---|
| 255 |
case Mode_Output: cns = mw_cns; is_input = false; break; |
|---|
| 256 |
case Mode_Update: cns = mrw_cns; is_input = false; break; |
|---|
| 257 |
default: assert(0); break; |
|---|
| 258 |
} |
|---|
| 259 |
break; |
|---|
| 260 |
case Arg_FrameRelative: |
|---|
| 261 |
// FIXME |
|---|
| 262 |
assert(0 && "asm fixme Arg_FrameRelative"); |
|---|
| 263 |
/* if (arg->expr->op == TOKvar) |
|---|
| 264 |
arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree; |
|---|
| 265 |
else |
|---|
| 266 |
assert(0); |
|---|
| 267 |
if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) { |
|---|
| 268 |
// arg_val = irs->integerConstant(var_frame_offset); |
|---|
| 269 |
cns = i_cns; |
|---|
| 270 |
} else { |
|---|
| 271 |
this->error("%s", "argument not frame relative"); |
|---|
| 272 |
return; |
|---|
| 273 |
} |
|---|
| 274 |
if (arg->mode != Mode_Input) |
|---|
| 275 |
clobbers_mem = true; |
|---|
| 276 |
break;*/ |
|---|
| 277 |
case Arg_LocalSize: |
|---|
| 278 |
// FIXME |
|---|
| 279 |
assert(0 && "asm fixme Arg_LocalSize"); |
|---|
| 280 |
/* var_frame_offset = cfun->x_frame_offset; |
|---|
| 281 |
if (var_frame_offset < 0) |
|---|
| 282 |
var_frame_offset = - var_frame_offset; |
|---|
| 283 |
arg_val = irs->integerConstant( var_frame_offset );*/ |
|---|
| 284 |
goto do_integer; |
|---|
| 285 |
default: |
|---|
| 286 |
assert(0); |
|---|
| 287 |
} |
|---|
| 288 |
|
|---|
| 289 |
if (is_input) { |
|---|
| 290 |
arg_map[i] = --input_idx; |
|---|
| 291 |
input_values.push_back(arg_val); |
|---|
| 292 |
input_constraints.push_back(cns); |
|---|
| 293 |
} else { |
|---|
| 294 |
arg_map[i] = n_outputs++; |
|---|
| 295 |
output_values.push_back(arg_val); |
|---|
| 296 |
output_constraints.push_back(cns); |
|---|
| 297 |
} |
|---|
| 298 |
} |
|---|
| 299 |
|
|---|
| 300 |
// Telling GCC that callee-saved registers are clobbered makes it preserve |
|---|
| 301 |
// those registers. This changes the stack from what a naked function |
|---|
| 302 |
// expects. |
|---|
| 303 |
|
|---|
| 304 |
// FIXME |
|---|
| 305 |
// if (! irs->func->naked) { |
|---|
| 306 |
assert(asmparser); |
|---|
| 307 |
for (int i = 0; i < code->regs.size(); i++) { |
|---|
| 308 |
if (code->regs[i]) { |
|---|
| 309 |
clobbers.push_back(asmparser->getRegName(i)); |
|---|
| 310 |
} |
|---|
| 311 |
} |
|---|
| 312 |
if (clobbers_mem) |
|---|
| 313 |
clobbers.push_back(memory_name); |
|---|
| 314 |
// } |
|---|
| 315 |
|
|---|
| 316 |
// Remap argument numbers |
|---|
| 317 |
for (unsigned i = 0; i < code->args.size(); i++) { |
|---|
| 318 |
if (arg_map[i] < 0) |
|---|
| 319 |
arg_map[i] = -arg_map[i] - 1 + n_outputs; |
|---|
| 320 |
} |
|---|
| 321 |
|
|---|
| 322 |
bool pct = false; |
|---|
| 323 |
std::string::iterator |
|---|
| 324 |
p = code->insnTemplate.begin(), |
|---|
| 325 |
q = code->insnTemplate.end(); |
|---|
| 326 |
//printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate); |
|---|
| 327 |
while (p < q) { |
|---|
| 328 |
if (pct) { |
|---|
| 329 |
if (*p >= '0' && *p <= '9') { |
|---|
| 330 |
// %% doesn't check against nargs |
|---|
| 331 |
*p = '0' + arg_map[*p - '0']; |
|---|
| 332 |
pct = false; |
|---|
| 333 |
} else if (*p == '$') { |
|---|
| 334 |
pct = false; |
|---|
| 335 |
} |
|---|
| 336 |
//assert(*p == '%');// could be 'a', etc. so forget it.. |
|---|
| 337 |
} else if (*p == '$') |
|---|
| 338 |
pct = true; |
|---|
| 339 |
++p; |
|---|
| 340 |
} |
|---|
| 341 |
|
|---|
| 342 |
typedef std::vector<std::string>::iterator It; |
|---|
| 343 |
if (Logger::enabled()) { |
|---|
| 344 |
Logger::cout() << "final asm: " << code->insnTemplate << '\n'; |
|---|
| 345 |
std::ostringstream ss; |
|---|
| 346 |
|
|---|
| 347 |
ss << "GCC-style output constraints: {"; |
|---|
| 348 |
for (It i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i) { |
|---|
| 349 |
ss << " " << *i; |
|---|
| 350 |
} |
|---|
| 351 |
ss << " }"; |
|---|
| 352 |
Logger::println("%s", ss.str().c_str()); |
|---|
| 353 |
|
|---|
| 354 |
ss.str(""); |
|---|
| 355 |
ss << "GCC-style input constraints: {"; |
|---|
| 356 |
for (It i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) { |
|---|
| 357 |
ss << " " << *i; |
|---|
| 358 |
} |
|---|
| 359 |
ss << " }"; |
|---|
| 360 |
Logger::println("%s", ss.str().c_str()); |
|---|
| 361 |
|
|---|
| 362 |
ss.str(""); |
|---|
| 363 |
ss << "GCC-style clobbers: {"; |
|---|
| 364 |
for (It i = clobbers.begin(), e = clobbers.end(); i != e; ++i) { |
|---|
| 365 |
ss << " " << *i; |
|---|
| 366 |
} |
|---|
| 367 |
ss << " }"; |
|---|
| 368 |
Logger::println("%s", ss.str().c_str()); |
|---|
| 369 |
} |
|---|
| 370 |
|
|---|
| 371 |
// rewrite GCC-style constraints to LLVM-style constraints |
|---|
| 372 |
std::string llvmOutConstraints; |
|---|
| 373 |
std::string llvmInConstraints; |
|---|
| 374 |
int n = 0; |
|---|
| 375 |
for(It i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i, ++n) { |
|---|
| 376 |
// rewrite update constraint to in and out constraints |
|---|
| 377 |
if((*i)[0] == '+') { |
|---|
| 378 |
assert(*i == mrw_cns && "What else are we updating except memory?"); |
|---|
| 379 |
/* LLVM doesn't support updating operands, so split into an input |
|---|
| 380 |
* and an output operand. |
|---|
| 381 |
*/ |
|---|
| 382 |
|
|---|
| 383 |
// Change update operand to pure output operand. |
|---|
| 384 |
*i = mw_cns; |
|---|
| 385 |
|
|---|
| 386 |
// Add input operand with same value, with original as "matching output". |
|---|
| 387 |
std::ostringstream ss; |
|---|
| 388 |
ss << '*' << (n + asmblock->outputcount); |
|---|
| 389 |
// Must be at the back; unused operands before used ones screw up numbering. |
|---|
| 390 |
input_constraints.push_back(ss.str()); |
|---|
| 391 |
input_values.push_back(output_values[n]); |
|---|
| 392 |
} |
|---|
| 393 |
llvmOutConstraints += *i; |
|---|
| 394 |
llvmOutConstraints += ","; |
|---|
| 395 |
} |
|---|
| 396 |
asmblock->outputcount += n; |
|---|
| 397 |
|
|---|
| 398 |
for(It i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) { |
|---|
| 399 |
llvmInConstraints += *i; |
|---|
| 400 |
llvmInConstraints += ","; |
|---|
| 401 |
} |
|---|
| 402 |
|
|---|
| 403 |
std::string clobstr; |
|---|
| 404 |
for(It i = clobbers.begin(), e = clobbers.end(); i != e; ++i) { |
|---|
| 405 |
clobstr = "~{" + *i + "},"; |
|---|
| 406 |
asmblock->clobs.insert(clobstr); |
|---|
| 407 |
} |
|---|
| 408 |
|
|---|
| 409 |
if (Logger::enabled()) { |
|---|
| 410 |
typedef std::vector<LLValue*>::iterator It; |
|---|
| 411 |
{ |
|---|
| 412 |
Logger::println("Output values:"); |
|---|
| 413 |
LOG_SCOPE |
|---|
| 414 |
size_t i = 0; |
|---|
| 415 |
for (It I = output_values.begin(), E = output_values.end(); I != E; ++I) { |
|---|
| 416 |
Logger::cout() << "Out " << i++ << " = " << **I << '\n'; |
|---|
| 417 |
} |
|---|
| 418 |
} |
|---|
| 419 |
{ |
|---|
| 420 |
Logger::println("Input values:"); |
|---|
| 421 |
LOG_SCOPE |
|---|
| 422 |
size_t i = 0; |
|---|
| 423 |
for (It I = input_values.begin(), E = input_values.end(); I != E; ++I) { |
|---|
| 424 |
Logger::cout() << "In " << i++ << " = " << **I << '\n'; |
|---|
| 425 |
} |
|---|
| 426 |
} |
|---|
| 427 |
} |
|---|
| 428 |
|
|---|
| 429 |
// excessive commas are removed later... |
|---|
| 430 |
|
|---|
| 431 |
// push asm statement |
|---|
| 432 |
IRAsmStmt* asmStmt = new IRAsmStmt; |
|---|
| 433 |
asmStmt->code = code->insnTemplate; |
|---|
| 434 |
asmStmt->out_c = llvmOutConstraints; |
|---|
| 435 |
asmStmt->in_c = llvmInConstraints; |
|---|
| 436 |
asmStmt->out.insert(asmStmt->out.begin(), output_values.begin(), output_values.end()); |
|---|
| 437 |
asmStmt->in.insert(asmStmt->in.begin(), input_values.begin(), input_values.end()); |
|---|
| 438 |
asmStmt->isBranchToLabel = isBranchToLabel; |
|---|
| 439 |
asmblock->s.push_back(asmStmt); |
|---|
| 440 |
} |
|---|
| 441 |
|
|---|
| 442 |
////////////////////////////////////////////////////////////////////////////// |
|---|
| 443 |
|
|---|
| 444 |
AsmBlockStatement::AsmBlockStatement(Loc loc, Statements* s) |
|---|
| 445 |
: CompoundStatement(loc, s) |
|---|
| 446 |
{ |
|---|
| 447 |
enclosingFinally = NULL; |
|---|
| 448 |
enclosingScopeExit = NULL; |
|---|
| 449 |
|
|---|
| 450 |
abiret = NULL; |
|---|
| 451 |
} |
|---|
| 452 |
|
|---|
| 453 |
// rewrite argument indices to the block scope indices |
|---|
| 454 |
static void remap_outargs(std::string& insnt, size_t nargs, size_t idx) |
|---|
| 455 |
{ |
|---|
| 456 |
static const std::string digits[10] = |
|---|
| 457 |
{ |
|---|
| 458 |
"0","1","2","3","4", |
|---|
| 459 |
"5","6","7","8","9" |
|---|
| 460 |
}; |
|---|
| 461 |
assert(nargs <= 10); |
|---|
| 462 |
|
|---|
| 463 |
static const std::string prefix("<<out"); |
|---|
| 464 |
static const std::string suffix(">>"); |
|---|
| 465 |
std::string argnum; |
|---|
| 466 |
std::string needle; |
|---|
| 467 |
char buf[10]; |
|---|
| 468 |
for (unsigned i = 0; i < nargs; i++) { |
|---|
| 469 |
needle = prefix + digits[i] + suffix; |
|---|
| 470 |
size_t pos = insnt.find(needle); |
|---|
| 471 |
if(std::string::npos != pos) |
|---|
| 472 |
sprintf(buf, "%lu", idx++); |
|---|
| 473 |
while(std::string::npos != (pos = insnt.find(needle))) |
|---|
| 474 |
insnt.replace(pos, needle.size(), buf); |
|---|
| 475 |
} |
|---|
| 476 |
} |
|---|
| 477 |
|
|---|
| 478 |
// rewrite argument indices to the block scope indices |
|---|
| 479 |
static void remap_inargs(std::string& insnt, size_t nargs, size_t idx) |
|---|
| 480 |
{ |
|---|
| 481 |
static const std::string digits[10] = |
|---|
| 482 |
{ |
|---|
| 483 |
"0","1","2","3","4", |
|---|
| 484 |
"5","6","7","8","9" |
|---|
| 485 |
}; |
|---|
| 486 |
assert(nargs <= 10); |
|---|
| 487 |
|
|---|
| 488 |
static const std::string prefix("<<in"); |
|---|
| 489 |
static const std::string suffix(">>"); |
|---|
| 490 |
std::string argnum; |
|---|
| 491 |
std::string needle; |
|---|
| 492 |
char buf[10]; |
|---|
| 493 |
for (unsigned i = 0; i < nargs; i++) { |
|---|
| 494 |
needle = prefix + digits[i] + suffix; |
|---|
| 495 |
size_t pos = insnt.find(needle); |
|---|
| 496 |
if(std::string::npos != pos) |
|---|
| 497 |
sprintf(buf, "%lu", idx++); |
|---|
| 498 |
while(std::string::npos != (pos = insnt.find(needle))) |
|---|
| 499 |
insnt.replace(pos, needle.size(), buf); |
|---|
| 500 |
} |
|---|
| 501 |
} |
|---|
| 502 |
|
|---|
| 503 |
LLValue* DtoAggrPairSwap(LLValue* aggr); |
|---|
| 504 |
|
|---|
| 505 |
void AsmBlockStatement::toIR(IRState* p) |
|---|
| 506 |
{ |
|---|
| 507 |
Logger::println("AsmBlockStatement::toIR(): %s", loc.toChars()); |
|---|
| 508 |
LOG_SCOPE; |
|---|
| 509 |
Logger::println("BEGIN ASM"); |
|---|
| 510 |
|
|---|
| 511 |
// disable inlining by default |
|---|
| 512 |
if (!p->func()->decl->allowInlining) |
|---|
| 513 |
p->func()->setNeverInline(); |
|---|
| 514 |
|
|---|
| 515 |
// create asm block structure |
|---|
| 516 |
assert(!p->asmBlock); |
|---|
| 517 |
IRAsmBlock* asmblock = new IRAsmBlock(this); |
|---|
| 518 |
assert(asmblock); |
|---|
| 519 |
p->asmBlock = asmblock; |
|---|
| 520 |
|
|---|
| 521 |
// do asm statements |
|---|
| 522 |
for (int i=0; i<statements->dim; i++) |
|---|
| 523 |
{ |
|---|
| 524 |
Statement* s = (Statement*)statements->data[i]; |
|---|
| 525 |
if (s) { |
|---|
| 526 |
s->toIR(p); |
|---|
| 527 |
} |
|---|
| 528 |
} |
|---|
| 529 |
|
|---|
| 530 |
// build forwarder for in-asm branches to external labels |
|---|
| 531 |
// this additional asm code sets the __llvm_jump_target variable |
|---|
| 532 |
// to a unique value that will identify the jump target in |
|---|
| 533 |
// a post-asm switch |
|---|
| 534 |
|
|---|
| 535 |
// maps each goto destination to its special value |
|---|
| 536 |
std::map<Identifier*, int> gotoToVal; |
|---|
| 537 |
|
|---|
| 538 |
// location of the special value determining the goto label |
|---|
| 539 |
// will be set if post-asm dispatcher block is needed |
|---|
| 540 |
llvm::AllocaInst* jump_target; |
|---|
| 541 |
|
|---|
| 542 |
{ |
|---|
| 543 |
FuncDeclaration* fd = gIR->func()->decl; |
|---|
| 544 |
char* fdmangle = fd->mangle(); |
|---|
| 545 |
|
|---|
| 546 |
// we use a simple static counter to make sure the new end labels are unique |
|---|
| 547 |
static size_t uniqueLabelsId = 0; |
|---|
| 548 |
std::ostringstream asmGotoEndLabel; |
|---|
| 549 |
asmGotoEndLabel << "." << fdmangle << "__llvm_asm_end" << uniqueLabelsId++; |
|---|
| 550 |
|
|---|
| 551 |
// initialize the setter statement we're going to build |
|---|
| 552 |
IRAsmStmt* outSetterStmt = new IRAsmStmt; |
|---|
| 553 |
std::string asmGotoEnd = "\n\tjmp "+asmGotoEndLabel.str()+"\n"; |
|---|
| 554 |
std::ostringstream code; |
|---|
| 555 |
code << asmGotoEnd; |
|---|
| 556 |
|
|---|
| 557 |
int n_goto = 1; |
|---|
| 558 |
|
|---|
| 559 |
size_t n = asmblock->s.size(); |
|---|
| 560 |
for(size_t i=0; i<n; ++i) |
|---|
| 561 |
{ |
|---|
| 562 |
IRAsmStmt* a = asmblock->s[i]; |
|---|
| 563 |
|
|---|
| 564 |
// skip non-branch statements |
|---|
| 565 |
if(!a->isBranchToLabel) |
|---|
| 566 |
continue; |
|---|
| 567 |
|
|---|
| 568 |
// if internal, no special handling is necessary, skip |
|---|
| 569 |
std::vector<Identifier*>::const_iterator it, end; |
|---|
| 570 |
end = asmblock->internalLabels.end(); |
|---|
| 571 |
bool skip = false; |
|---|
| 572 |
for(it = asmblock->internalLabels.begin(); it != end; ++it) |
|---|
| 573 |
if((*it)->equals(a->isBranchToLabel)) |
|---|
| 574 |
skip = true; |
|---|
| 575 |
if(skip) |
|---|
| 576 |
continue; |
|---|
| 577 |
|
|---|
| 578 |
// if we already set things up for this branch target, skip |
|---|
| 579 |
if(gotoToVal.find(a->isBranchToLabel) != gotoToVal.end()) |
|---|
| 580 |
continue; |
|---|
| 581 |
|
|---|
| 582 |
// record that the jump needs to be handled in the post-asm dispatcher |
|---|
| 583 |
gotoToVal[a->isBranchToLabel] = n_goto; |
|---|
| 584 |
|
|---|
| 585 |
// provide an in-asm target for the branch and set value |
|---|
| 586 |
Logger::println("statement '%s' references outer label '%s': creating forwarder", a->code.c_str(), a->isBranchToLabel->string); |
|---|
| 587 |
code << fdmangle << '_' << a->isBranchToLabel->string << ":\n\t"; |
|---|
| 588 |
code << "movl $<<in" << n_goto << ">>, $<<out0>>\n"; |
|---|
| 589 |
//FIXME: Store the value -> label mapping somewhere, so it can be referenced later |
|---|
| 590 |
outSetterStmt->in.push_back(DtoConstUint(n_goto)); |
|---|
| 591 |
outSetterStmt->in_c += "i,"; |
|---|
| 592 |
code << asmGotoEnd; |
|---|
| 593 |
|
|---|
| 594 |
++n_goto; |
|---|
| 595 |
} |
|---|
| 596 |
if(code.str() != asmGotoEnd) |
|---|
| 597 |
{ |
|---|
| 598 |
// finalize code |
|---|
| 599 |
outSetterStmt->code = code.str(); |
|---|
| 600 |
outSetterStmt->code += asmGotoEndLabel.str()+":\n"; |
|---|
| 601 |
|
|---|
| 602 |
// create storage for and initialize the temporary |
|---|
| 603 |
jump_target = DtoAlloca(Type::tint32, "__llvm_jump_target"); |
|---|
| 604 |
gIR->ir->CreateStore(DtoConstUint(0), jump_target); |
|---|
| 605 |
// setup variable for output from asm |
|---|
| 606 |
outSetterStmt->out_c = "=*m,"; |
|---|
| 607 |
outSetterStmt->out.push_back(jump_target); |
|---|
| 608 |
|
|---|
| 609 |
asmblock->s.push_back(outSetterStmt); |
|---|
| 610 |
} |
|---|
| 611 |
else |
|---|
| 612 |
delete outSetterStmt; |
|---|
| 613 |
} |
|---|
| 614 |
|
|---|
| 615 |
|
|---|
| 616 |
// build a fall-off-end-properly asm statement |
|---|
| 617 |
|
|---|
| 618 |
FuncDeclaration* thisfunc = p->func()->decl; |
|---|
| 619 |
bool useabiret = false; |
|---|
| 620 |
p->asmBlock->asmBlock->abiret = NULL; |
|---|
| 621 |
if (thisfunc->fbody->endsWithAsm() == this && thisfunc->type->nextOf()->ty != Tvoid) |
|---|
| 622 |
{ |
|---|
| 623 |
// there can't be goto forwarders in this case |
|---|
| 624 |
assert(gotoToVal.empty()); |
|---|
| 625 |
emitABIReturnAsmStmt(asmblock, loc, thisfunc); |
|---|
| 626 |
useabiret = true; |
|---|
| 627 |
} |
|---|
| 628 |
|
|---|
| 629 |
|
|---|
| 630 |
// build asm block |
|---|
| 631 |
std::vector<LLValue*> outargs; |
|---|
| 632 |
std::vector<LLValue*> inargs; |
|---|
| 633 |
std::vector<const LLType*> outtypes; |
|---|
| 634 |
std::vector<const LLType*> intypes; |
|---|
| 635 |
std::string out_c; |
|---|
| 636 |
std::string in_c; |
|---|
| 637 |
std::string clobbers; |
|---|
| 638 |
std::string code; |
|---|
| 639 |
size_t asmIdx = asmblock->retn; |
|---|
| 640 |
|
|---|
| 641 |
Logger::println("do outputs"); |
|---|
| 642 |
size_t n = asmblock->s.size(); |
|---|
| 643 |
for (size_t i=0; i<n; ++i) |
|---|
| 644 |
{ |
|---|
| 645 |
IRAsmStmt* a = asmblock->s[i]; |
|---|
| 646 |
assert(a); |
|---|
| 647 |
size_t onn = a->out.size(); |
|---|
| 648 |
for (size_t j=0; j<onn; ++j) |
|---|
| 649 |
{ |
|---|
| 650 |
outargs.push_back(a->out[j]); |
|---|
| 651 |
outtypes.push_back(a->out[j]->getType()); |
|---|
| 652 |
} |
|---|
| 653 |
if (!a->out_c.empty()) |
|---|
| 654 |
{ |
|---|
| 655 |
out_c += a->out_c; |
|---|
| 656 |
} |
|---|
| 657 |
remap_outargs(a->code, onn+a->in.size(), asmIdx); |
|---|
| 658 |
asmIdx += onn; |
|---|
| 659 |
} |
|---|
| 660 |
|
|---|
| 661 |
Logger::println("do inputs"); |
|---|
| 662 |
for (size_t i=0; i<n; ++i) |
|---|
| 663 |
{ |
|---|
| 664 |
IRAsmStmt* a = asmblock->s[i]; |
|---|
| 665 |
assert(a); |
|---|
| 666 |
size_t inn = a->in.size(); |
|---|
| 667 |
for (size_t j=0; j<inn; ++j) |
|---|
| 668 |
{ |
|---|
| 669 |
inargs.push_back(a->in[j]); |
|---|
| 670 |
intypes.push_back(a->in[j]->getType()); |
|---|
| 671 |
} |
|---|
| 672 |
if (!a->in_c.empty()) |
|---|
| 673 |
{ |
|---|
| 674 |
in_c += a->in_c; |
|---|
| 675 |
} |
|---|
| 676 |
remap_inargs(a->code, inn+a->out.size(), asmIdx); |
|---|
| 677 |
asmIdx += inn; |
|---|
| 678 |
if (!code.empty()) |
|---|
| 679 |
code += "\n\t"; |
|---|
| 680 |
code += a->code; |
|---|
| 681 |
} |
|---|
| 682 |
asmblock->s.clear(); |
|---|
| 683 |
|
|---|
| 684 |
// append inputs |
|---|
| 685 |
out_c += in_c; |
|---|
| 686 |
|
|---|
| 687 |
// append clobbers |
|---|
| 688 |
typedef std::set<std::string>::iterator clobs_it; |
|---|
| 689 |
for (clobs_it i=asmblock->clobs.begin(); i!=asmblock->clobs.end(); ++i) |
|---|
| 690 |
{ |
|---|
| 691 |
out_c += *i; |
|---|
| 692 |
} |
|---|
| 693 |
|
|---|
| 694 |
// remove excessive comma |
|---|
| 695 |
if (!out_c.empty()) |
|---|
| 696 |
out_c.resize(out_c.size()-1); |
|---|
| 697 |
|
|---|
| 698 |
Logger::println("code = \"%s\"", code.c_str()); |
|---|
| 699 |
Logger::println("constraints = \"%s\"", out_c.c_str()); |
|---|
| 700 |
|
|---|
| 701 |
// build return types |
|---|
| 702 |
const LLType* retty; |
|---|
| 703 |
if (asmblock->retn) |
|---|
| 704 |
retty = asmblock->retty; |
|---|
| 705 |
else |
|---|
| 706 |
retty = llvm::Type::getVoidTy(gIR->context()); |
|---|
| 707 |
|
|---|
| 708 |
// build argument types |
|---|
| 709 |
std::vector<const LLType*> types; |
|---|
| 710 |
types.insert(types.end(), outtypes.begin(), outtypes.end()); |
|---|
| 711 |
types.insert(types.end(), intypes.begin(), intypes.end()); |
|---|
| 712 |
llvm::FunctionType* fty = llvm::FunctionType::get(retty, types, false); |
|---|
| 713 |
if (Logger::enabled()) |
|---|
| 714 |
Logger::cout() << "function type = " << *fty << '\n'; |
|---|
| 715 |
|
|---|
| 716 |
std::vector<LLValue*> args; |
|---|
| 717 |
args.insert(args.end(), outargs.begin(), outargs.end()); |
|---|
| 718 |
args.insert(args.end(), inargs.begin(), inargs.end()); |
|---|
| 719 |
|
|---|
| 720 |
if (Logger::enabled()) { |
|---|
| 721 |
Logger::cout() << "Arguments:" << '\n'; |
|---|
| 722 |
Logger::indent(); |
|---|
| 723 |
for (std::vector<LLValue*>::iterator b = args.begin(), i = b, e = args.end(); i != e; ++i) { |
|---|
| 724 |
Stream cout = Logger::cout(); |
|---|
| 725 |
cout << '$' << (i - b) << " ==> " << **i; |
|---|
| 726 |
if (!llvm::isa<llvm::Instruction>(*i) && !llvm::isa<LLGlobalValue>(*i)) |
|---|
| 727 |
cout << '\n'; |
|---|
| 728 |
} |
|---|
| 729 |
Logger::undent(); |
|---|
| 730 |
} |
|---|
| 731 |
|
|---|
| 732 |
llvm::InlineAsm* ia = llvm::InlineAsm::get(fty, code, out_c, true); |
|---|
| 733 |
|
|---|
| 734 |
llvm::CallInst* call = p->ir->CreateCall(ia, args.begin(), args.end(), |
|---|
| 735 |
retty == LLType::getVoidTy(gIR->context()) ? "" : "asm"); |
|---|
| 736 |
|
|---|
| 737 |
if (Logger::enabled()) |
|---|
| 738 |
Logger::cout() << "Complete asm statement: " << *call << '\n'; |
|---|
| 739 |
|
|---|
| 740 |
// capture abi return value |
|---|
| 741 |
if (useabiret) |
|---|
| 742 |
{ |
|---|
| 743 |
IRAsmBlock* block = p->asmBlock; |
|---|
| 744 |
if (block->retfixup) |
|---|
| 745 |
block->asmBlock->abiret = (*block->retfixup)(p->ir, call); |
|---|
| 746 |
else if (p->asmBlock->retemu) |
|---|
| 747 |
block->asmBlock->abiret = DtoLoad(block->asmBlock->abiret); |
|---|
| 748 |
else |
|---|
| 749 |
block->asmBlock->abiret = call; |
|---|
| 750 |
} |
|---|
| 751 |
|
|---|
| 752 |
p->asmBlock = NULL; |
|---|
| 753 |
Logger::println("END ASM"); |
|---|
| 754 |
|
|---|
| 755 |
// if asm contained external branches, emit goto forwarder code |
|---|
| 756 |
if(!gotoToVal.empty()) |
|---|
| 757 |
{ |
|---|
| 758 |
assert(jump_target); |
|---|
| 759 |
|
|---|
| 760 |
// make new blocks |
|---|
| 761 |
llvm::BasicBlock* oldend = gIR->scopeend(); |
|---|
| 762 |
llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterasmgotoforwarder", p->topfunc(), oldend); |
|---|
| 763 |
|
|---|
| 764 |
llvm::LoadInst* val = p->ir->CreateLoad(jump_target, "__llvm_jump_target_value"); |
|---|
| 765 |
llvm::SwitchInst* sw = p->ir->CreateSwitch(val, bb, gotoToVal.size()); |
|---|
| 766 |
|
|---|
| 767 |
// add all cases |
|---|
| 768 |
std::map<Identifier*, int>::iterator it, end = gotoToVal.end(); |
|---|
| 769 |
for(it = gotoToVal.begin(); it != end; ++it) |
|---|
| 770 |
{ |
|---|
| 771 |
llvm::BasicBlock* casebb = llvm::BasicBlock::Create(gIR->context(), "case", p->topfunc(), bb); |
|---|
| 772 |
sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), it->second), casebb); |
|---|
| 773 |
|
|---|
| 774 |
p->scope() = IRScope(casebb,bb); |
|---|
| 775 |
DtoGoto(loc, it->first, enclosingFinally); |
|---|
| 776 |
} |
|---|
| 777 |
|
|---|
| 778 |
p->scope() = IRScope(bb,oldend); |
|---|
| 779 |
} |
|---|
| 780 |
} |
|---|
| 781 |
|
|---|
| 782 |
// the whole idea of this statement is to avoid the flattening |
|---|
| 783 |
Statements* AsmBlockStatement::flatten(Scope* sc) |
|---|
| 784 |
{ |
|---|
| 785 |
return NULL; |
|---|
| 786 |
} |
|---|
| 787 |
|
|---|
| 788 |
Statement *AsmBlockStatement::syntaxCopy() |
|---|
| 789 |
{ |
|---|
| 790 |
Statements *a = new Statements(); |
|---|
| 791 |
a->setDim(statements->dim); |
|---|
| 792 |
for (size_t i = 0; i < statements->dim; i++) |
|---|
| 793 |
{ |
|---|
| 794 |
Statement *s = (Statement *)statements->data[i]; |
|---|
| 795 |
if (s) |
|---|
| 796 |
s = s->syntaxCopy(); |
|---|
| 797 |
a->data[i] = s; |
|---|
| 798 |
} |
|---|
| 799 |
AsmBlockStatement *cs = new AsmBlockStatement(loc, a); |
|---|
| 800 |
return cs; |
|---|
| 801 |
} |
|---|
| 802 |
|
|---|
| 803 |
// necessary for in-asm branches |
|---|
| 804 |
Statement *AsmBlockStatement::semantic(Scope *sc) |
|---|
| 805 |
{ |
|---|
| 806 |
enclosingFinally = sc->enclosingFinally; |
|---|
| 807 |
enclosingScopeExit = sc->enclosingScopeExit; |
|---|
| 808 |
|
|---|
| 809 |
return CompoundStatement::semantic(sc); |
|---|
| 810 |
} |
|---|
| 811 |
|
|---|
| 812 |
////////////////////////////////////////////////////////////////////////////// |
|---|
| 813 |
|
|---|
| 814 |
void AsmStatement::toNakedIR(IRState *p) |
|---|
| 815 |
{ |
|---|
| 816 |
Logger::println("AsmStatement::toNakedIR(): %s", loc.toChars()); |
|---|
| 817 |
LOG_SCOPE; |
|---|
| 818 |
|
|---|
| 819 |
// is there code? |
|---|
| 820 |
if (!asmcode) |
|---|
| 821 |
return; |
|---|
| 822 |
AsmCode * code = (AsmCode *) asmcode; |
|---|
| 823 |
|
|---|
| 824 |
// build asm stmt |
|---|
| 825 |
p->nakedAsm << "\t" << code->insnTemplate << std::endl; |
|---|
| 826 |
} |
|---|
| 827 |
|
|---|
| 828 |
void AsmBlockStatement::toNakedIR(IRState *p) |
|---|
| 829 |
{ |
|---|
| 830 |
Logger::println("AsmBlockStatement::toNakedIR(): %s", loc.toChars()); |
|---|
| 831 |
LOG_SCOPE; |
|---|
| 832 |
|
|---|
| 833 |
// do asm statements |
|---|
| 834 |
for (unsigned i=0; i<statements->dim; i++) |
|---|
| 835 |
{ |
|---|
| 836 |
Statement* s = (Statement*)statements->data[i]; |
|---|
| 837 |
if (s) s->toNakedIR(p); |
|---|
| 838 |
} |
|---|
| 839 |
} |
|---|