Reapply "[clang] Improve handling of physical registers in inline

assembly operands."

Earlyclobbers are now excepted from this change (original commit: c78da03).

Review: Ulrich Weigand, Nick Desaulniers

Differential Revision: https://reviews.llvm.org/D87279
This commit is contained in:
Jonas Paulsson 2020-10-14 08:48:29 +02:00
parent 58f6b16c49
commit 42a82862b6
4 changed files with 50 additions and 3 deletions

View File

@ -21,6 +21,7 @@
#include "clang/Basic/PrettyStackTrace.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/InlineAsm.h"
@ -1953,7 +1954,8 @@ SimplifyConstraint(const char *Constraint, const TargetInfo &Target,
static std::string
AddVariableConstraints(const std::string &Constraint, const Expr &AsmExpr,
const TargetInfo &Target, CodeGenModule &CGM,
const AsmStmt &Stmt, const bool EarlyClobber) {
const AsmStmt &Stmt, const bool EarlyClobber,
std::string *GCCReg = nullptr) {
const DeclRefExpr *AsmDeclRef = dyn_cast<DeclRefExpr>(&AsmExpr);
if (!AsmDeclRef)
return Constraint;
@ -1978,6 +1980,8 @@ AddVariableConstraints(const std::string &Constraint, const Expr &AsmExpr,
}
// Canonicalize the register here before returning it.
Register = Target.getNormalizedGCCRegisterName(Register);
if (GCCReg != nullptr)
*GCCReg = Register.str();
return (EarlyClobber ? "&{" : "{") + Register.str() + "}";
}
@ -2176,6 +2180,9 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
// Keep track of out constraints for tied input operand.
std::vector<std::string> OutputConstraints;
// Keep track of defined physregs.
llvm::SmallSet<std::string, 8> PhysRegOutputs;
// An inline asm can be marked readonly if it meets the following conditions:
// - it doesn't have any sideeffects
// - it doesn't clobber memory
@ -2195,9 +2202,15 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
const Expr *OutExpr = S.getOutputExpr(i);
OutExpr = OutExpr->IgnoreParenNoopCasts(getContext());
std::string GCCReg;
OutputConstraint = AddVariableConstraints(OutputConstraint, *OutExpr,
getTarget(), CGM, S,
Info.earlyClobber());
Info.earlyClobber(),
&GCCReg);
// Give an error on multiple outputs to same physreg.
if (!GCCReg.empty() && !PhysRegOutputs.insert(GCCReg).second)
CGM.Error(S.getAsmLoc(), "multiple outputs to hard register: " + GCCReg);
OutputConstraints.push_back(OutputConstraint);
LValue Dest = EmitLValue(OutExpr);
if (!Constraints.empty())
@ -2284,7 +2297,8 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
LargestVectorWidth =
std::max((uint64_t)LargestVectorWidth,
VT->getPrimitiveSizeInBits().getKnownMinSize());
if (Info.allowsRegister())
// Only tie earlyclobber physregs.
if (Info.allowsRegister() && (GCCReg.empty() || Info.earlyClobber()))
InOutConstraints += llvm::utostr(i);
else
InOutConstraints += OutputConstraint;

View File

@ -74,3 +74,9 @@ void test_gcc_registers(void) {
asm volatile("mov r0, r1\n");
// CHECK: call void asm sideeffect "mov r0, r1\0A", ""()
}
void test_tied_earlyclobber(void) {
register int a asm("x1");
asm("" : "+&r"(a));
// CHECK: call i32 asm "", "=&{x1},0"(i32 %0)
}

View File

@ -0,0 +1,13 @@
// RUN: not %clang_cc1 -triple s390x-linux-gnu -O2 -emit-llvm -o - %s 2>&1 \
// RUN: | FileCheck %s
// REQUIRES: systemz-registered-target
// Test that an error is given if a physreg is defined by multiple operands.
int test_physreg_defs(void) {
register int l __asm__("r7") = 0;
// CHECK: error: multiple outputs to hard register: r7
__asm__("" : "+r"(l), "=r"(l));
return l;
}

View File

@ -129,3 +129,17 @@ long double test_f128(long double f, long double g) {
// CHECK: [[RESULT:%.*]] = tail call fp128 asm "axbr $0, $2", "=f,0,f"(fp128 %f, fp128 %g)
// CHECK: store fp128 [[RESULT]], fp128* [[DEST]]
}
// Test that there are no tied physreg uses. TwoAddress pass cannot deal with them.
int test_physregs(void) {
// CHECK-LABEL: define signext i32 @test_physregs()
register int l __asm__("r7") = 0;
// CHECK: call i32 asm "lr $0, $1", "={r7},{r7}"
__asm__("lr %0, %1" : "+r"(l));
// CHECK: call i32 asm "$0 $1 $2", "={r7},{r7},{r7}"
__asm__("%0 %1 %2" : "+r"(l) : "r"(l));
return l;
}