ldc/gen/asm-gcc.cpp

265 lines
7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- asm-gcc.cpp -------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// Converts a GDC/GCC-style inline assembly statement to an LLVM inline
// assembler expression.
//
//===----------------------------------------------------------------------===//
#include "dmd/errors.h"
#include "dmd/expression.h"
#include "dmd/statement.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
namespace {
llvm::StringRef peekString(StringExp *se) {
DString slice = se->peekString();
return {slice.ptr, slice.length};
}
// Translates a GCC inline asm template string to LLVM's expected format.
std::string translateTemplate(GccAsmStatement *stmt) {
const auto insn = peekString(stmt->insn->isStringExp());
const auto N = insn.size();
std::string result;
result.reserve(static_cast<size_t>(N * 1.2));
for (size_t i = 0; i < N; ++i) {
const char c = insn[i];
switch (c) {
case '$':
result += "$$"; // escape for LLVM: $ => $$
break;
case '%':
if (i < N - 1 && insn[i + 1] == '%') { // unescape for LLVM: %% => %
result += '%';
++i;
} else {
result += '$'; // e.g., %0 => $0
}
break;
default:
result += c;
break;
}
}
return result;
}
class ConstraintsBuilder {
bool isAnyX86;
std::ostringstream str; // LLVM constraints string being built
LLSmallVector<bool, 8> _isIndirectOperand;
// Appends a constraint string expression with an optional prefix.
// Returns true if the string describes an indirect operand.
bool append(Expression *e, char prefix = 0) {
#if LDC_LLVM_VER >= 1800
#define startswith starts_with
#endif
auto se = e->isStringExp();
assert(se);
llvm::StringRef code = peekString(se);
assert(!code.empty());
// commit prefix and strip from `code`, if present
if (prefix) {
str << prefix;
if (code[0] == prefix)
code = code.substr(1);
}
// commit any modifier and strip from `code`
bool isIndirect = false;
if (code.startswith("&")) { // early clobber
str << '&';
code = code.substr(1);
} else if (code.startswith("*")) { // indirect in/output
isIndirect = true; // delay the commit
code = code.substr(1);
}
const std::string name = translateName(code, isIndirect);
if (isIndirect)
str << '*';
str << name;
str << ',';
return isIndirect;
#if LDC_LLVM_VER >= 1800
#undef startswith
#endif
}
// Might set `isIndirect` to true (but never resets to false).
std::string translateName(llvm::StringRef gccName, bool &isIndirect) const {
// clang translates GCC `m` to LLVM `*m` (indirect operand)
if (gccName == "m") {
isIndirect = true;
return "m";
}
// some variable-width x86[_64] GCC register names aren't supported by LLVM
// directly
if (isAnyX86 && gccName.size() == 1) {
switch (gccName[0]) {
case 'a':
return "{ax}";
case 'b':
return "{bx}";
case 'c':
return "{cx}";
case 'd':
return "{dx}";
case 'S':
return "{si}";
case 'D':
return "{di}";
default:
break;
}
}
return needsCurlyBraces(gccName) ? ("{" + gccName + "}").str()
: gccName.str();
}
// Register names need to be enclosed in curly braces for LLVM.
bool needsCurlyBraces(llvm::StringRef gccName) const {
auto N = gccName.size();
if (N == 1 || (N == 3 && gccName[0] == '^'))
return false;
return !gccName.contains('{');
}
public:
ConstraintsBuilder() {
auto arch = global.params.targetTriple->getArch();
isAnyX86 = (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64);
}
// Returns the final constraints string for LLVM for a GCC-style asm
// statement.
std::string build(GccAsmStatement *stmt) {
str.clear();
_isIndirectOperand.clear();
if (auto c = stmt->constraints) {
_isIndirectOperand.reserve(c->length);
for (size_t i = 0; i < c->length; ++i) {
bool isOutput = (i < stmt->outputargs);
bool isIndirect = append((*c)[i], isOutput ? '=' : 0);
_isIndirectOperand.push_back(isIndirect);
}
}
if (stmt->clobbers) {
for (auto e : *stmt->clobbers) {
append(e, '~');
}
}
// remove excessive comma
std::string result = str.str();
if (auto size = result.size())
result.resize(size - 1);
return result;
}
bool isIndirectOperand(size_t operandIndex) const {
return _isIndirectOperand[operandIndex];
}
};
}
void GccAsmStatement_toIR(GccAsmStatement *stmt, IRState *irs) {
IF_LOG Logger::println("GccAsmStatement::toIR(): %s", stmt->loc.toChars());
LOG_SCOPE;
if (stmt->labels) {
error(stmt->loc,
"goto labels for GCC-style asm statements are not supported yet");
fatal();
}
if (stmt->names) {
for (Identifier *name : *stmt->names) {
if (name) {
error(stmt->loc,
"symbolic names for operands in GCC-style assembly are not "
"supported yet");
fatal();
}
}
}
const std::string insn = translateTemplate(stmt);
ConstraintsBuilder constraintsBuilder;
const std::string constraints = constraintsBuilder.build(stmt);
LLSmallVector<LLValue *, 8> outputLVals;
LLSmallVector<LLType *, 8> outputTypes;
LLSmallVector<LLType *, 8> indirectTypes;
LLSmallVector<LLValue *, 8> operands;
if (stmt->args) {
for (size_t i = 0; i < stmt->args->length; ++i) {
Expression *e = (*stmt->args)[i];
const bool isOutput = (i < stmt->outputargs);
const bool isIndirect = constraintsBuilder.isIndirectOperand(i);
if (isOutput) {
assert(e->isLvalue() && "should have been caught by front-end");
LLValue *lval = DtoLVal(e);
if (isIndirect) {
operands.push_back(lval);
indirectTypes.push_back(DtoType(e->type));
} else {
outputLVals.push_back(lval);
outputTypes.push_back(DtoType(e->type));
}
} else {
if (isIndirect && !e->isLvalue()) {
error(e->loc,
"indirect `\"m\"` input operands require an lvalue, but `%s` "
"is an rvalue",
e->toChars());
fatal();
}
LLValue *inputVal = isIndirect ? DtoLVal(e) : DtoRVal(e);
operands.push_back(inputVal);
if (isIndirect)
indirectTypes.push_back(DtoType(e->type));
}
}
}
const size_t N = outputTypes.size();
LLType *returnType =
N == 0 ? llvm::Type::getVoidTy(irs->context())
: N == 1 ? outputTypes[0]
: LLStructType::get(irs->context(), outputTypes);
LLValue *rval =
DtoInlineAsmExpr(stmt->loc, insn, constraints, operands,
indirectTypes, returnType);
if (N == 1) {
DtoStore(rval, outputLVals[0]);
} else {
for (size_t i = 0; i < N; ++i) {
auto element = DtoExtractValue(rval, i);
DtoStore(element, outputLVals[i]);
}
}
}