diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 4b813be086e122..3f8f77654f6d37 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -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(&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 OutputConstraints; + // Keep track of defined physregs. + llvm::SmallSet 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; diff --git a/clang/test/CodeGen/aarch64-inline-asm.c b/clang/test/CodeGen/aarch64-inline-asm.c index 0889a7157f0b17..a6e8faef8b9e20 100644 --- a/clang/test/CodeGen/aarch64-inline-asm.c +++ b/clang/test/CodeGen/aarch64-inline-asm.c @@ -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) +} diff --git a/clang/test/CodeGen/systemz-inline-asm-02.c b/clang/test/CodeGen/systemz-inline-asm-02.c new file mode 100644 index 00000000000000..754d7e66f04b24 --- /dev/null +++ b/clang/test/CodeGen/systemz-inline-asm-02.c @@ -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; +} diff --git a/clang/test/CodeGen/systemz-inline-asm.c b/clang/test/CodeGen/systemz-inline-asm.c index c5497655f505f4..357fd4c184e709 100644 --- a/clang/test/CodeGen/systemz-inline-asm.c +++ b/clang/test/CodeGen/systemz-inline-asm.c @@ -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; +}