diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index c4b86b203d383..e5d1dbc9b7d4f 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2256,6 +2256,49 @@ Query for this feature with ``__has_extension(gnu_asm_constexpr_strings)``. asm((std::string_view("nop")) ::: (std::string_view("memory"))); } +Hard Register Operands for ASM Constraints +========================================== + +Clang supports the ability to specify specific hardware registers in inline +assembly constraints via the use of curly braces ``{}``. These register names +are the same names as what would be used in the clobber list. + +Prior to clang-19, the only way to associate an inline assembly constraint +with a specific register is via the local register variable feature (`GCC +Specifying Registers for Local Variables `_). +However, the local register variable association lasts for the entire +scope of the variable. + +Hard register operands will instead only apply to the specific inline ASM +statement which improves readability and solves a few other issues experienced +by local register variables, such as: + +* the constraints for the register operands are superfluous +* one register variable cannot be used for 2 different inline + assemblies if the value is expected in different hard regs + +The code below is an example of an inline assembly statement using local +register variables: + +.. code-block:: c++ + + void foo() { + register int *p1 asm ("r0") = bar(); + register int *p2 asm ("r1") = bar(); + register int *result asm ("r0"); + asm ("sysint" : "=r" (result) : "0" (p1), "r" (p2)); + } + +Below is the same code but using hard register operands. + +.. code-block:: c++ + + void foo() { + int *p1 = bar(); + int *p2 = bar(); + int *result; + asm ("sysint" : "={r0}" (result) : "0" (p1), "{r1}" (p2)); + } Objective-C Features ==================== diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 381d1fb063eba..075ced1d4abdf 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9941,6 +9941,8 @@ let CategoryName = "Inline Assembly Issue" in { "more than one input constraint matches the same output '%0'">; def err_store_value_to_reg : Error< "impossible constraint in asm: cannot store value into a register">; + def err_asm_hard_reg_variable_duplicate : Error< + "hard register operand already defined as register variable">; def warn_asm_label_on_auto_decl : Warning< "ignored asm label '%0' on automatic variable">; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 4ff77bb64cf1c..7343b8c54d754 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1117,9 +1117,17 @@ class TargetInfo : public TransferrableTargetInfo, /// /// This function is used by Sema in order to diagnose conflicts between /// the clobber list and the input/output lists. + /// The constraint should already by validated in + /// validateHardRegisterAsmConstraint so just do some basic checking virtual StringRef getConstraintRegister(StringRef Constraint, StringRef Expression) const { - return ""; + StringRef Reg = Expression; + size_t Start = Constraint.find('{'); + size_t End = Constraint.find('}'); + if (Start != StringRef::npos && End != StringRef::npos && End > Start) + Reg = Constraint.substr(Start + 1, End - Start - 1); + + return Reg; } struct ConstraintInfo { @@ -1279,6 +1287,14 @@ class TargetInfo : public TransferrableTargetInfo, validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &info) const = 0; + // Validate the "hard register" inline asm constraint. This constraint is + // of the form {}. This constraint is meant to be used + // as an alternative for the "register asm" construct to put inline + // asm operands into specific registers. + bool + validateHardRegisterAsmConstraint(const char *&Name, + TargetInfo::ConstraintInfo &info) const; + bool resolveSymbolicName(const char *&Name, ArrayRef OutputConstraints, unsigned &Index) const; diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 3b95f8dbc4415..29b89e474b458 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -839,6 +839,18 @@ bool TargetInfo::validateOutputConstraint(ConstraintInfo &Info) const { case 'E': case 'F': break; // Pass them. + case '{': { + // First, check the target parser in case it validates + // the {...} constraint differently. + if (validateAsmConstraint(Name, Info)) + return true; + + // If not, that's okay, we will try to validate it + // using a target agnostic implementation. + if (!validateHardRegisterAsmConstraint(Name, Info)) + return false; + break; + } } Name++; @@ -854,6 +866,36 @@ bool TargetInfo::validateOutputConstraint(ConstraintInfo &Info) const { return Info.allowsMemory() || Info.allowsRegister(); } +bool TargetInfo::validateHardRegisterAsmConstraint( + const char *&Name, TargetInfo::ConstraintInfo &Info) const { + // First, swallow the '{'. + Name++; + + // Mark the start of the possible register name. + const char *Start = Name; + + // Loop through rest of "Name". + // In this loop, we check whether we have a closing curly brace which + // validates the constraint. Also, this allows us to get the correct bounds to + // set our register name. + while (*Name && *Name != '}') + Name++; + + // Missing '}', return false. + if (!*Name) + return false; + + // Now we set the register name. + std::string Register(Start, Name - Start); + + // We validate whether its a valid register to be used. + if (!isValidGCCRegisterName(Register)) + return false; + + Info.setAllowsRegister(); + return true; +} + bool TargetInfo::resolveSymbolicName(const char *&Name, ArrayRef OutputConstraints, unsigned &Index) const { @@ -986,6 +1028,18 @@ bool TargetInfo::validateInputConstraint( case '!': // Disparage severely. case '*': // Ignore for choosing register preferences. break; // Pass them. + case '{': { + // First, check the target parser in case it validates + // the {...} constraint differently. + if (validateAsmConstraint(Name, Info)) + return true; + + // If not, that's okay, we will try to validate it + // using a target agnostic implementation. + if (!validateHardRegisterAsmConstraint(Name, Info)) + return false; + break; + } } Name++; @@ -1067,6 +1121,14 @@ void TargetInfo::copyAuxTarget(const TargetInfo *Aux) { std::string TargetInfo::simplifyConstraint(StringRef Constraint, SmallVectorImpl *OutCons) const { + // If we have only the {...} constraint, do not do any simplifications. This + // already maps to the lower level LLVM inline assembly IR that tells the + // backend to allocate a specific register. Any validations would have already + // been done in the Sema stage or will be done in the AddVariableConstraints + // function. + if (Constraint[0] == '{' || (Constraint[0] == '&' && Constraint[1] == '{')) + return std::string(Constraint); + std::string Result; for (const char *I = Constraint.begin(), *E = Constraint.end(); I < E; I++) { diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index d44b66d1b124e..fb29a6b5b3fea 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -231,11 +231,6 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo { std::string &SuggestedModifier) const override; std::string_view getClobbers() const override; - StringRef getConstraintRegister(StringRef Constraint, - StringRef Expression) const override { - return Expression; - } - int getEHDataRegisterNumber(unsigned RegNo) const override; bool validatePointerAuthKey(const llvm::APSInt &value) const override; diff --git a/clang/lib/Basic/Targets/ARM.h b/clang/lib/Basic/Targets/ARM.h index 43c4718f4735b..ff5d4bd712c53 100644 --- a/clang/lib/Basic/Targets/ARM.h +++ b/clang/lib/Basic/Targets/ARM.h @@ -205,11 +205,6 @@ class LLVM_LIBRARY_VISIBILITY ARMTargetInfo : public TargetInfo { std::string &SuggestedModifier) const override; std::string_view getClobbers() const override; - StringRef getConstraintRegister(StringRef Constraint, - StringRef Expression) const override { - return Expression; - } - CallingConvCheckResult checkCallingConvention(CallingConv CC) const override; int getEHDataRegisterNumber(unsigned RegNo) const override; diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h index 685735b54a45b..c152b51fdbe01 100644 --- a/clang/lib/Basic/Targets/RISCV.h +++ b/clang/lib/Basic/Targets/RISCV.h @@ -70,11 +70,6 @@ class RISCVTargetInfo : public TargetInfo { std::string_view getClobbers() const override { return ""; } - StringRef getConstraintRegister(StringRef Constraint, - StringRef Expression) const override { - return Expression; - } - ArrayRef getGCCRegNames() const override; int getEHDataRegisterNumber(unsigned RegNo) const override { diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index 1806709b1070c..c58a4e186be0b 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -312,7 +312,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo { return "di"; // In case the constraint is 'r' we need to return Expression case 'r': - return Expression; + return TargetInfo::getConstraintRegister(Constraint, Expression); // Double letters Y constraints case 'Y': if ((++I != E) && ((*I == '0') || (*I == 'z'))) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index c050fd41ac0e9..2d847a54889b2 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -2556,41 +2556,125 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) { CaseRangeBlock = SavedCRBlock; } -/// AddVariableConstraints - Look at AsmExpr and if it is a variable declared -/// as using a particular register add that as a constraint that will be used -/// in this asm stmt. -static std::string -AddVariableConstraints(const std::string &Constraint, const Expr &AsmExpr, - const TargetInfo &Target, CodeGenModule &CGM, - const AsmStmt &Stmt, const bool EarlyClobber, - std::string *GCCReg = nullptr) { +/// Is it valid to apply a register constraint for a variable marked with +/// the "register asm" construct? +/// Optionally, if it is determined that we can, we set "Register" to the +/// regiser name. +static bool +ShouldApplyRegisterVariableConstraint(const Expr &AsmExpr, + std::string *Register = nullptr) { + const DeclRefExpr *AsmDeclRef = dyn_cast(&AsmExpr); if (!AsmDeclRef) - return Constraint; + return false; const ValueDecl &Value = *AsmDeclRef->getDecl(); const VarDecl *Variable = dyn_cast(&Value); if (!Variable) - return Constraint; + return false; if (Variable->getStorageClass() != SC_Register) - return Constraint; + return false; AsmLabelAttr *Attr = Variable->getAttr(); if (!Attr) + return false; + + if (Register != nullptr) + // Set the register to return from Attr. + *Register = Attr->getLabel().str(); + return true; +} + +/// AddVariableConstraints: +/// Look at AsmExpr and if it is a variable declared as using a particular +/// register add that as a constraint that will be used in this asm stmt. +/// Whether it can be used or not is dependent on querying +/// ShouldApplyRegisterVariableConstraint() Also check whether the "hard +/// register" inline asm constraint (i.e. "{reg-name}") is specified. If so, add +/// that as a constraint that will be used in this asm stmt. +static std::string +AddVariableConstraints(const std::string &Constraint, const Expr &AsmExpr, + const TargetInfo &Target, CodeGenModule &CGM, + const AsmStmt &Stmt, const bool EarlyClobber, + SmallVector *GCCRegs = nullptr) { + + StringRef Str(Constraint); + StringRef::iterator I = Str.begin(), E = Str.end(); + // Do we have the "hard register" inline asm constraint. + StringRef::iterator HardRegStart = std::find(I, E, '{'); + StringRef::iterator HardRegEnd = std::find(I, E, '}'); + // Do we have at least one hard register. + bool ApplyHardRegisterConstraint = + HardRegStart != E && HardRegEnd != E && HardRegEnd > HardRegStart; + + // Do we have "register asm" on a variable. + std::string Reg = ""; + bool ApplyRegisterVariableConstraint = + ShouldApplyRegisterVariableConstraint(AsmExpr, &Reg); + + // Diagnose the scenario where we apply both the register variable constraint + // and a hard register variable constraint as an unsupported error. + // Why? Because we could have a situation where the register passed in through + // {...} and the register passed in through the "register asm" construct could + // be different, and in this case, there's no way for the compiler to know + // which one to emit. + if (ApplyHardRegisterConstraint && ApplyRegisterVariableConstraint) { + CGM.getDiags().Report(AsmExpr.getExprLoc(), + diag::err_asm_hard_reg_variable_duplicate); + return Constraint; + } + + if (!ApplyHardRegisterConstraint && !ApplyRegisterVariableConstraint) return Constraint; - StringRef Register = Attr->getLabel(); - assert(Target.isValidGCCRegisterName(Register)); + // We're using validateOutputConstraint here because we only care if // this is a register constraint. TargetInfo::ConstraintInfo Info(Constraint, ""); - if (Target.validateOutputConstraint(Info) && - !Info.allowsRegister()) { + if (Target.validateOutputConstraint(Info) && !Info.allowsRegister()) { CGM.ErrorUnsupported(&Stmt, "__asm__"); return Constraint; } - // Canonicalize the register here before returning it. - Register = Target.getNormalizedGCCRegisterName(Register); - if (GCCReg != nullptr) - *GCCReg = Register.str(); - return (EarlyClobber ? "&{" : "{") + Register.str() + "}"; + + if (ApplyRegisterVariableConstraint) { + StringRef Register(Reg); + assert(Target.isValidGCCRegisterName(Register)); + // Canonicalize the register here before returning it. + Register = Target.getNormalizedGCCRegisterName(Register); + if (GCCRegs) + GCCRegs->push_back(Register.str()); + return (EarlyClobber ? "&{" : "{") + Register.str() + "}"; + } + + std::string NC; + while (I != E) { + if (*I == '{') { + HardRegEnd = std::find(I + 1, E, '}'); + // No error checking because we already validated this constraint + StringRef Register(I + 1, HardRegEnd - I - 1); + // If we don't have a valid register name, simply return the constraint. + // For example: There are some targets like X86 that use a constraint such + // as "@cca", which is validated and then converted into {@cca}. Now this + // isn't necessarily a "GCC Register", but in terms of emission, it is + // valid since it lowered appropriately in the X86 backend. For the {..} + // constraint, we shouldn't be too strict and error out if the register + // itself isn't a valid "GCC register". + if (!Target.isValidGCCRegisterName(Register)) + return Constraint; + + // Canonicalize the register here before returning it. + Register = Target.getNormalizedGCCRegisterName(Register); + // Do not need to worry about early clobber since the symbol should be + // copied from the original constraint string + NC += "{" + Register.str() + "}"; + + if (GCCRegs) + GCCRegs->push_back(Register.str()); + + I = HardRegEnd + 1; + } else { + NC += *I; + ++I; + } + } + return NC; } std::pair CodeGenFunction::EmitAsmInputLValue( @@ -2943,14 +3027,17 @@ 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(), - &GCCReg); + SmallVector GCCRegs; + OutputConstraint = + AddVariableConstraints(OutputConstraint, *OutExpr, getTarget(), CGM, S, + Info.earlyClobber(), &GCCRegs); // 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); + if (!GCCRegs.empty()) { + for (auto R : GCCRegs) { + if (!PhysRegOutputs.insert(R).second) + CGM.Error(S.getAsmLoc(), "multiple outputs to hard register: " + R); + } + } OutputConstraints.push_back(OutputConstraint); LValue Dest = EmitLValue(OutExpr); @@ -3061,7 +3148,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { std::max((uint64_t)LargestVectorWidth, VT->getPrimitiveSizeInBits().getKnownMinValue()); // Only tie earlyclobber physregs. - if (Info.allowsRegister() && (GCCReg.empty() || Info.earlyClobber())) + if (Info.allowsRegister() && (GCCRegs.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 8ddee560b11da..860cc858275ea 100644 --- a/clang/test/CodeGen/AArch64/inline-asm.c +++ b/clang/test/CodeGen/AArch64/inline-asm.c @@ -77,7 +77,15 @@ void test_gcc_registers(void) { void test_tied_earlyclobber(void) { register int a asm("x1"); - asm("" : "+&r"(a)); + asm("" + : "+&r"(a)); + // CHECK: call i32 asm "", "=&{x1},0"(i32 %0) +} + +void test_tied_earlyclobber2(void) { + int a; + asm("" + : "+&{x1}"(a)); // CHECK: call i32 asm "", "=&{x1},0"(i32 %0) } @@ -102,4 +110,4 @@ void test_sme_constraints(){ asm("movt zt0[3, mul vl], z0" : : : "zt0"); // CHECK: call void asm sideeffect "movt zt0[3, mul vl], z0", "~{zt0}"() -} \ No newline at end of file +} diff --git a/clang/test/CodeGen/SystemZ/systemz-inline-asm-02.c b/clang/test/CodeGen/SystemZ/systemz-inline-asm-02.c index 754d7e66f04b2..f0951944cfd22 100644 --- a/clang/test/CodeGen/SystemZ/systemz-inline-asm-02.c +++ b/clang/test/CodeGen/SystemZ/systemz-inline-asm-02.c @@ -5,9 +5,23 @@ // 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; + int m; // CHECK: error: multiple outputs to hard register: r7 - __asm__("" : "+r"(l), "=r"(l)); + __asm__("" + : "+r"(l), "=r"(l)); - return l; + // CHECK: error: multiple outputs to hard register: r6 + __asm__("" + : "+{r6}"(m), "={r6}"(m)); + + // CHECK: error: multiple outputs to hard register: r6 + __asm__("" + : "+{r1}{r2}{r6}"(m), "={r6}"(m)); + + // CHECK: error: multiple outputs to hard register: r6 + __asm__("" + : "+{r6}"(m), "={r6}{r1}{r2}"(m)); + + return l + m; } diff --git a/clang/test/CodeGen/SystemZ/systemz-inline-asm.c b/clang/test/CodeGen/SystemZ/systemz-inline-asm.c index d76fb4bd1fda6..8d82249314ba4 100644 --- a/clang/test/CodeGen/SystemZ/systemz-inline-asm.c +++ b/clang/test/CodeGen/SystemZ/systemz-inline-asm.c @@ -142,12 +142,25 @@ long double test_f128(long double f, long double g) { int test_physregs(void) { // CHECK-LABEL: define{{.*}} signext i32 @test_physregs() register int l __asm__("r7") = 0; + int m = 0; // CHECK: call i32 asm "lr $0, $1", "={r7},{r7}" - __asm__("lr %0, %1" : "+r"(l)); + __asm__("lr %0, %1" + : "+r"(l)); // CHECK: call i32 asm "$0 $1 $2", "={r7},{r7},{r7}" - __asm__("%0 %1 %2" : "+r"(l) : "r"(l)); + __asm__("%0 %1 %2" + : "+r"(l) + : "r"(l)); - return l; + // CHECK: call i32 asm "lr $0, $1", "={r6},{r6}" + __asm__("lr %0, %1" + : "+{r6}"(m)); + + // CHECK: call i32 asm "$0 $1 $2", "={r6},{r6},{r6}" + __asm__("%0 %1 %2" + : "+{r6}"(m) + : "{r6}"(m)); + + return l + m; } diff --git a/clang/test/CodeGen/asm-goto.c b/clang/test/CodeGen/asm-goto.c index 4037c1b2a3d7a..77bd77615f299 100644 --- a/clang/test/CodeGen/asm-goto.c +++ b/clang/test/CodeGen/asm-goto.c @@ -55,14 +55,14 @@ int test3(int out1, int out2) { int test4(int out1, int out2) { // CHECK-LABEL: define{{.*}} i32 @test4( - // CHECK: callbr { i32, i32 } asm sideeffect "jne ${5:l}", "={si},={di},r,0,1,!i,!i + // CHECK: callbr { i32, i32 } asm sideeffect "jne ${5:l}", "={si},={di},r,{si},{di},!i,!i // CHECK: to label %asm.fallthrough [label %label_true.split, label %loop.split] // CHECK-LABEL: asm.fallthrough: if (out1 < out2) asm volatile goto("jne %l5" : "+S"(out1), "+D"(out2) : "r"(out1) :: label_true, loop); else asm volatile goto("jne %l7" : "+S"(out1), "+D"(out2) : "r"(out1), "r"(out2) :: label_true, loop); - // CHECK: callbr { i32, i32 } asm sideeffect "jne ${7:l}", "={si},={di},r,r,0,1,!i,!i + // CHECK: callbr { i32, i32 } asm sideeffect "jne ${7:l}", "={si},={di},r,r,{si},{di},!i,!i // CHECK: to label %asm.fallthrough6 [label %label_true.split11, label %loop.split14] // CHECK-LABEL: asm.fallthrough6: return out1 + out2; @@ -92,7 +92,7 @@ int test5(int addr, int size, int limit) { int test6(int out1) { // CHECK-LABEL: define{{.*}} i32 @test6( - // CHECK: callbr i32 asm sideeffect "testl $0, $0; testl $1, $1; jne ${3:l}", "={si},r,0,!i,!i,{{.*}} + // CHECK: callbr i32 asm sideeffect "testl $0, $0; testl $1, $1; jne ${3:l}", "={si},r,{si},!i,!i,{{.*}} // CHECK: to label %asm.fallthrough [label %label_true.split, label %landing.split] // CHECK-LABEL: asm.fallthrough: // CHECK-LABEL: landing: diff --git a/clang/test/CodeGen/ms-intrinsics.c b/clang/test/CodeGen/ms-intrinsics.c index b86662ee6778a..69a2920c0389c 100644 --- a/clang/test/CodeGen/ms-intrinsics.c +++ b/clang/test/CodeGen/ms-intrinsics.c @@ -36,12 +36,12 @@ void test__movsb(unsigned char *Dest, unsigned char *Src, size_t Count) { return __movsb(Dest, Src, Count); } // CHECK-I386-LABEL: define{{.*}} void @test__movsb -// CHECK-I386: tail call { ptr, ptr, i32 } asm sideeffect "xchg $(%esi, $1$|$1, esi$)\0Arep movsb\0Axchg $(%esi, $1$|$1, esi$)", "={di},=r,={cx},0,1,2,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i32 %Count) +// CHECK-I386: tail call { ptr, ptr, i32 } asm sideeffect "xchg $(%esi, $1$|$1, esi$)\0Arep movsb\0Axchg $(%esi, $1$|$1, esi$)", "={di},=r,={cx},{di},1,{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i32 %Count) // CHECK-I386: ret void // CHECK-I386: } // CHECK-X64-LABEL: define{{.*}} void @test__movsb -// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movsb", "={di},={si},={cx},0,1,2,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) +// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movsb", "={di},={si},={cx},{di},{si},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) // CHECK-X64: ret void // CHECK-X64: } @@ -49,12 +49,12 @@ void test__stosw(unsigned short *Dest, unsigned short Data, size_t Count) { return __stosw(Dest, Data, Count); } // CHECK-I386-LABEL: define{{.*}} void @test__stosw -// CHECK-I386: call { ptr, i32 } asm sideeffect "rep stosw", "={di},={cx},{ax},0,1,~{memory},~{dirflag},~{fpsr},~{flags}"(i16 %Data, ptr %Dest, i32 %Count) +// CHECK-I386: call { ptr, i32 } asm sideeffect "rep stosw", "={di},={cx},{ax},{di},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(i16 %Data, ptr %Dest, i32 %Count) // CHECK-I386: ret void // CHECK-I386: } // CHECK-X64-LABEL: define{{.*}} void @test__stosw -// CHECK-X64: call { ptr, i64 } asm sideeffect "rep stosw", "={di},={cx},{ax},0,1,~{memory},~{dirflag},~{fpsr},~{flags}"(i16 %Data, ptr %Dest, i64 %Count) +// CHECK-X64: call { ptr, i64 } asm sideeffect "rep stosw", "={di},={cx},{ax},{di},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(i16 %Data, ptr %Dest, i64 %Count) // CHECK-X64: ret void // CHECK-X64: } @@ -62,12 +62,12 @@ void test__movsw(unsigned short *Dest, unsigned short *Src, size_t Count) { return __movsw(Dest, Src, Count); } // CHECK-I386-LABEL: define{{.*}} void @test__movsw -// CHECK-I386: tail call { ptr, ptr, i32 } asm sideeffect "xchg $(%esi, $1$|$1, esi$)\0Arep movsw\0Axchg $(%esi, $1$|$1, esi$)", "={di},=r,={cx},0,1,2,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i32 %Count) +// CHECK-I386: tail call { ptr, ptr, i32 } asm sideeffect "xchg $(%esi, $1$|$1, esi$)\0Arep movsw\0Axchg $(%esi, $1$|$1, esi$)", "={di},=r,={cx},{di},1,{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i32 %Count) // CHECK-I386: ret void // CHECK-I386: } // CHECK-X64-LABEL: define{{.*}} void @test__movsw -// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movsw", "={di},={si},={cx},0,1,2,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) +// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movsw", "={di},={si},={cx},{di},{si},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) // CHECK-X64: ret void // CHECK-X64: } @@ -75,12 +75,12 @@ void test__stosd(unsigned long *Dest, unsigned long Data, size_t Count) { return __stosd(Dest, Data, Count); } // CHECK-I386-LABEL: define{{.*}} void @test__stosd -// CHECK-I386: call { ptr, i32 } asm sideeffect "rep stos$(l$|d$)", "={di},={cx},{ax},0,1,~{memory},~{dirflag},~{fpsr},~{flags}"(i32 %Data, ptr %Dest, i32 %Count) +// CHECK-I386: call { ptr, i32 } asm sideeffect "rep stos$(l$|d$)", "={di},={cx},{ax},{di},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(i32 %Data, ptr %Dest, i32 %Count) // CHECK-I386: ret void // CHECK-I386: } // CHECK-X64-LABEL: define{{.*}} void @test__stosd -// CHECK-X64: call { ptr, i64 } asm sideeffect "rep stos$(l$|d$)", "={di},={cx},{ax},0,1,~{memory},~{dirflag},~{fpsr},~{flags}"(i32 %Data, ptr %Dest, i64 %Count) +// CHECK-X64: call { ptr, i64 } asm sideeffect "rep stos$(l$|d$)", "={di},={cx},{ax},{di},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(i32 %Data, ptr %Dest, i64 %Count) // CHECK-X64: ret void // CHECK-X64: } @@ -88,12 +88,12 @@ void test__movsd(unsigned long *Dest, unsigned long *Src, size_t Count) { return __movsd(Dest, Src, Count); } // CHECK-I386-LABEL: define{{.*}} void @test__movsd -// CHECK-I386: tail call { ptr, ptr, i32 } asm sideeffect "xchg $(%esi, $1$|$1, esi$)\0Arep movs$(l$|d$)\0Axchg $(%esi, $1$|$1, esi$)", "={di},=r,={cx},0,1,2,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i32 %Count) +// CHECK-I386: tail call { ptr, ptr, i32 } asm sideeffect "xchg $(%esi, $1$|$1, esi$)\0Arep movs$(l$|d$)\0Axchg $(%esi, $1$|$1, esi$)", "={di},=r,={cx},{di},1,{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i32 %Count) // CHECK-I386: ret void // CHECK-I386: } // CHECK-X64-LABEL: define{{.*}} void @test__movsd -// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movs$(l$|d$)", "={di},={si},={cx},0,1,2,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) +// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movs$(l$|d$)", "={di},={si},={cx},{di},{si},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) // CHECK-X64: ret void // CHECK-X64: } @@ -102,7 +102,7 @@ void test__stosq(unsigned __int64 *Dest, unsigned __int64 Data, size_t Count) { return __stosq(Dest, Data, Count); } // CHECK-X64-LABEL: define{{.*}} void @test__stosq -// CHECK-X64: call { ptr, i64 } asm sideeffect "rep stosq", "={di},={cx},{ax},0,1,~{memory},~{dirflag},~{fpsr},~{flags}"(i64 %Data, ptr %Dest, i64 %Count) +// CHECK-X64: call { ptr, i64 } asm sideeffect "rep stosq", "={di},={cx},{ax},{di},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(i64 %Data, ptr %Dest, i64 %Count) // CHECK-X64: ret void // CHECK-X64: } @@ -110,7 +110,7 @@ void test__movsq(unsigned __int64 *Dest, unsigned __int64 *Src, size_t Count) { return __movsq(Dest, Src, Count); } // CHECK-X64-LABEL: define{{.*}} void @test__movsq -// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movsq", "={di},={si},={cx},0,1,2,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) +// CHECK-X64: call { ptr, ptr, i64 } asm sideeffect "rep movsq", "={di},={si},={cx},{di},{si},{cx},~{memory},~{dirflag},~{fpsr},~{flags}"(ptr %Dest, ptr %Src, i64 %Count) // CHECK-X64: ret void // CHECK-X64: } #endif @@ -698,13 +698,13 @@ long test_InterlockedExchange_HLERelease(long volatile *Target, long Value) { long test_InterlockedCompareExchange_HLEAcquire(long volatile *Destination, long Exchange, long Comparand) { // CHECK-INTEL: define{{.*}} i32 @test_InterlockedCompareExchange_HLEAcquire(ptr{{.*}}%Destination, i32{{[a-z_ ]*}}%Exchange, i32{{[a-z_ ]*}}%Comparand) -// CHECK-INTEL: call i32 asm sideeffect ".byte 0xf2 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,0,*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %Destination, i32 %Exchange, i32 %Comparand, ptr elementtype(i32) %Destination) +// CHECK-INTEL: call i32 asm sideeffect ".byte 0xf2 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,{ax},*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %Destination, i32 %Exchange, i32 %Comparand, ptr elementtype(i32) %Destination) return _InterlockedCompareExchange_HLEAcquire(Destination, Exchange, Comparand); } long test_InterlockedCompareExchange_HLERelease(long volatile *Destination, long Exchange, long Comparand) { // CHECK-INTEL: define{{.*}} i32 @test_InterlockedCompareExchange_HLERelease(ptr{{.*}}%Destination, i32{{[a-z_ ]*}}%Exchange, i32{{[a-z_ ]*}}%Comparand) -// CHECK-INTEL: call i32 asm sideeffect ".byte 0xf3 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,0,*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %Destination, i32 %Exchange, i32 %Comparand, ptr elementtype(i32) %Destination) +// CHECK-INTEL: call i32 asm sideeffect ".byte 0xf3 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,{ax},*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %Destination, i32 %Exchange, i32 %Comparand, ptr elementtype(i32) %Destination) return _InterlockedCompareExchange_HLERelease(Destination, Exchange, Comparand); } #endif @@ -722,13 +722,13 @@ __int64 test_InterlockedExchange64_HLERelease(__int64 volatile *Target, __int64 __int64 test_InterlockedCompareExchange64_HLEAcquire(__int64 volatile *Destination, __int64 Exchange, __int64 Comparand) { // CHECK-X64: define{{.*}} i64 @test_InterlockedCompareExchange64_HLEAcquire(ptr{{.*}}%Destination, i64{{[a-z_ ]*}}%Exchange, i64{{[a-z_ ]*}}%Comparand) -// CHECK-X64: call i64 asm sideeffect ".byte 0xf2 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,0,*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i64) %Destination, i64 %Exchange, i64 %Comparand, ptr elementtype(i64) %Destination) +// CHECK-X64: call i64 asm sideeffect ".byte 0xf2 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,{ax},*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i64) %Destination, i64 %Exchange, i64 %Comparand, ptr elementtype(i64) %Destination) return _InterlockedCompareExchange64_HLEAcquire(Destination, Exchange, Comparand); } __int64 test_InterlockedCompareExchange64_HLERelease(__int64 volatile *Destination, __int64 Exchange, __int64 Comparand) { // CHECK-X64: define{{.*}} i64 @test_InterlockedCompareExchange64_HLERelease(ptr{{.*}}%Destination, i64{{[a-z_ ]*}}%Exchange, i64{{[a-z_ ]*}}%Comparand) -// CHECK-X64: call i64 asm sideeffect ".byte 0xf3 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,0,*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i64) %Destination, i64 %Exchange, i64 %Comparand, ptr elementtype(i64) %Destination) +// CHECK-X64: call i64 asm sideeffect ".byte 0xf3 ; lock ; cmpxchg $($2, $1$|$1, $2$)", "={ax},=*m,r,{ax},*m,~{memory},~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i64) %Destination, i64 %Exchange, i64 %Comparand, ptr elementtype(i64) %Destination) return _InterlockedCompareExchange64_HLERelease(Destination, Exchange, Comparand); } #endif diff --git a/clang/test/CodeGen/x86-asm-register-constraint-mix.c b/clang/test/CodeGen/x86-asm-register-constraint-mix.c new file mode 100644 index 0000000000000..038a978349c9a --- /dev/null +++ b/clang/test/CodeGen/x86-asm-register-constraint-mix.c @@ -0,0 +1,62 @@ +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -O2 -emit-llvm %s -o - | FileCheck %s + +unsigned long foo(unsigned long addr, unsigned long a0, + unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, + unsigned long a5) { + register unsigned long result asm("rax"); + register unsigned long addr1 asm("rax") = addr; + register unsigned long b0 asm("rdi") = a0; + register unsigned long b1 asm("rsi") = a1; + register unsigned long b2 asm("rdx") = a2; + register unsigned long b3 asm("rcx") = a3; + register unsigned long b4 asm("r8") = a4; + register unsigned long b5 asm("r9") = a5; + + // CHECK: tail call i64 asm "call *$1", "={rax},{rax},{rdi},{rsi},{rdx},{rcx},{r8},{r9},{rax},~{dirflag},~{fpsr},~{flags}"(i64 %addr, i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 undef) + asm("call *%1" + : "+r" (result) + : "r"(addr1), "r"(b0), "r"(b1), "r"(b2), "r"(b3), "r"(b4), "r"(b5)); + return result; +} + +unsigned long foo1(unsigned long addr, unsigned long a0, + unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, + unsigned long a5) { + unsigned long result; + unsigned long addr1 = addr; + unsigned long b0 = a0; + unsigned long b1 = a1; + unsigned long b2 = a2; + unsigned long b3 = a3; + unsigned long b4 = a4; + unsigned long b5 = a5; + + // CHECK: tail call i64 asm "call *$1", "={rax},{rax},{rdi},{rsi},{rdx},{rcx},{r8},{r9},{rax},~{dirflag},~{fpsr},~{flags}"(i64 %addr, i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 undef) + asm("call *%1" + : "+{rax}" (result) + : "{rax}"(addr1), "{rdi}"(b0), "{rsi}"(b1), "{rdx}"(b2), "{rcx}"(b3), "{r8}"(b4), "{r9}"(b5)); + return result; +} + +unsigned long foo2(unsigned long addr, unsigned long a0, + unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, + unsigned long a5) { + register unsigned long result asm("rax"); + unsigned long addr1 = addr; + unsigned long b0 = a0; + register unsigned long b1 asm ("rsi") = a1; + unsigned long b2 = a2; + unsigned long b3 = a3; + register unsigned long b4 asm ("r8") = a4; + unsigned long b5 = a5; + + // CHECK: tail call i64 asm "call *$1", "={rax},{rax},{rdi},{rsi},{rdx},{rcx},{r8},{r9},{rax},~{dirflag},~{fpsr},~{flags}"(i64 %addr, i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 undef) + asm("call *%1" + : "+r" (result) + : "{rax}"(addr1), "{rdi}"(b0), "r"(b1), "{rdx}"(b2), "{rcx}"(b3), "r"(b4), "{r9}"(b5)); + return result; +} diff --git a/clang/test/CodeGen/z-hard-register-inline-asm.c b/clang/test/CodeGen/z-hard-register-inline-asm.c new file mode 100644 index 0000000000000..bf70af5765f64 --- /dev/null +++ b/clang/test/CodeGen/z-hard-register-inline-asm.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-COUNT %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-COUNT-2 %s + +void f1() { + int a, b; + register int c asm("r1"); + register int d asm("r2"); + + // CHECK-COUNT: call i32 asm "lhi $0,5\0A", "={r1}" + // CHECK-COUNT-2: call i32 asm "lhi $0,5\0A", "={r1}" + __asm("lhi %0,5\n" + : "={r1}"(a) + : + :); + __asm("lhi %0,5\n" + : "=r"(c) + : + :); + + // CHECK-COUNT: call i32 asm "lgr $0,$1\0A", "={r1},{r2}" + // CHECK-COUNT-2: call i32 asm "lgr $0,$1\0A", "={r1},{r2}" + __asm("lgr %0,%1\n" + : "={r1}"(a) + : "{r2}"(b) + :); + __asm("lgr %0,%1\n" + : "=r"(c) + : "r"(d) + :); + + // CHECK-COUNT: call i32 asm "lgr $0,$1\0A", "={r1},{r2}" + // CHECK-COUNT-2: call i32 asm "lgr $0,$1\0A", "={r1},{r2}" + __asm("lgr %0,%1\n" + : "={%r1}"(a) + : "{%r2}"(b) + :); + __asm("lgr %0,%1\n" + : "={r1}"(a) + : "{%r2}"(b) + :); + + // CHECK-COUNT: call i32 asm "lgr $0,$1\0A", "=&{r1},{r2}" + // CHECK-COUNT-2: call i32 asm "lgr $0,$1\0A", "=&{r1},{r2}" + __asm("lgr %0,%1\n" + : "=&{r1}"(a) + : "{%r2}"(b) + :); + __asm("lgr %0,%1\n" + : "=&r"(c) + : "r"(d) + :); +} diff --git a/clang/test/CodeGen/z-multi-hard-register-inline-asm.c b/clang/test/CodeGen/z-multi-hard-register-inline-asm.c new file mode 100644 index 0000000000000..1d49d02c0086d --- /dev/null +++ b/clang/test/CodeGen/z-multi-hard-register-inline-asm.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-COUNT %s + +void f1() { + int a, b; + + // CHECK-COUNT: call i32 asm "lhi $0,5\0A", "={r1}{r2}" + __asm("lhi %0,5\n" + : "={r1}{r2}"(a) + : + :); + + // CHECK-COUNT: call i32 asm "lgr $0,$1\0A", "={r1}{r3},{r2}{r4}" + __asm("lgr %0,%1\n" + : "={r1}{r3}"(a) + : "{r2}{r4}"(b) + :); + + // CHECK-COUNT: call i32 asm "lgr $0,$1\0A", "={r1}{r3},{r2}{r4}" + __asm("lgr %0,%1\n" + : "={%r1}{%r3}"(a) + : "{%r2}{%r4}"(b) + :); + + // CHECK-COUNT: call i32 asm "lgr $0,$1\0A", "=&{r1}{r3},{r2}" + __asm("lgr %0,%1\n" + : "=&{r1}{r3}"(a) + : "{%r2}"(b) + :); + + // CHECK-COUNT: call i32 asm "lgr $0,$1\0A", "=r{r1}{r3},{r2}r{r4}" + __asm("lgr %0,%1\n" + : "=r{r1}{r3}"(a) + : "{r2}r{r4}"(b) + :); +} diff --git a/clang/test/Sema/z-hard-register-inline-asm.c b/clang/test/Sema/z-hard-register-inline-asm.c new file mode 100644 index 0000000000000..e27ecc1c832b7 --- /dev/null +++ b/clang/test/Sema/z-hard-register-inline-asm.c @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 %s -triple s390x-ibm-linux -fsyntax-only -verify +// RUN: %clang_cc1 %s -triple s390x-ibm-zos -fsyntax-only -verify + +void f1() { + int a, b; + __asm("lhi %0,5\n" + : "={r2}"(a) + :); + + __asm("lgr %0,%1\n" + : "={r2}"(a) + : "{r1}"(b)); + + __asm("lgr %0,%1\n" + : "={r2}"(a) + : "{%r1}"(b)); + + __asm("lgr %0,%1\n" + : "=&{r1}"(a) + : "{r2}"(b)); + + __asm("lhi %0,5\n" + : "={r2"(a) // expected-error {{invalid output constraint '={r2' in asm}} + :); + + __asm("lhi %0,5\n" + : "={r17}"(a) // expected-error {{invalid output constraint '={r17}' in asm}} + :); + + __asm("lhi %0,5\n" + : "={}"(a) // expected-error {{invalid output constraint '={}' in asm}} + :); + + __asm("lhi %0,5\n" + : "=&{r2"(a) // expected-error {{invalid output constraint '=&{r2' in asm}} + :); + + __asm("lgr %0,%1\n" + : "=r"(a) + : "{r1"(b)); // expected-error {{invalid input constraint '{r1' in asm}} + + __asm("lgr %0,%1\n" + : "=r"(a) + : "{}"(b)); // expected-error {{invalid input constraint '{}' in asm}} + + __asm("lgr %0,%1\n" + : "={r1}"(a) + : "{r17}"(b)); // expected-error {{invalid input constraint '{r17}' in asm}} +} diff --git a/clang/test/Sema/z-multi-hard-register-inline-asm.c b/clang/test/Sema/z-multi-hard-register-inline-asm.c new file mode 100644 index 0000000000000..385eeaf745302 --- /dev/null +++ b/clang/test/Sema/z-multi-hard-register-inline-asm.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 %s -triple s390x-ibm-linux -fsyntax-only -verify +// RUN: %clang_cc1 %s -triple s390x-ibm-zos -fsyntax-only -verify + +void f1() { + int a, b; + + __asm("lgr %0,%1\n" + : "={r2}"(a) + : "{r1}{r3}"(b)); + + __asm("lgr %0,%1\n" + : "={r2}{r1}"(a) + : "{r1}"(b)); + + __asm("lgr %0,%1\n" + : "={r2}"(a) + : "{r1}{r3}r4}"(b)); // expected-error {{invalid input constraint '{r1}{r3}r4}' in asm}} + + __asm("lgr %0,%1\n" + : "={r2}"(a) + : "{r1r3}{r4}"(b)); // expected-error {{invalid input constraint '{r1r3}{r4}' in asm}} + + __asm("lgr %0,%1\n" + : "={r1}{r3}r4}"(a) // expected-error {{invalid output constraint '={r1}{r3}r4}' in asm}} + : "{r2}"(b)); + + __asm("lgr %0,%1\n" + : "={r1r3}{r4}"(a) // expected-error {{invalid output constraint '={r1r3}{r4}' in asm}} + : "{r2}"(b)); +} diff --git a/llvm/test/CodeGen/SystemZ/zos-inline-asm.ll b/llvm/test/CodeGen/SystemZ/zos-inline-asm.ll new file mode 100644 index 0000000000000..4d849730bd827 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/zos-inline-asm.ll @@ -0,0 +1,261 @@ +; RUN: llc -mtriple s390x-ibm-zos < %s | FileCheck %s +; Source to generate .ll file +; +; void f1() { +; int a, b; +; __asm(" lhi %0,5\n" +; : "={r1}"(a) +; : +; :); +; +; __asm(" lgr %0,%1\n" +; : "={r1}"(a) +; : "{r2}"(b) +; :); +; +; __asm(" lgr %0,%1\n" +; : "=r{r1}{r2}"(a) +; : "{r4}{r5}"(b) +; :); +; } +; +; void f2() { +; int a, m_b; +; __asm(" stg %1,%0\n" +; : "=m"(m_b) +; : "{r1}"(a) +; :); +; } +; +; void f3() { +; int r15, r1; +; +; __asm(" svc 109\n" +; : "={r15}"(r15) +; : "{r1}"(r1), "{r15}"(25) +; :); +; } +; +; void f4() { +; ptr parm; +; long long rc, reason; +; char *code; +; +; __asm(" pc 0(%3)" +; : "={r0}"(reason), "+{r1}"(parm), "={r15}"(rc) +; : "r"(code) +; :); +; } +; +; void f5() { +; +; int a; +; int b; +; int c; +; +; __asm(" lhi %0,10\n" +; " ar %0,%0\n" +; : "=&r"(a) +; : +; :); +; +; __asm(" lhi %0,10\n" +; " ar %0,%0\n" +; : "={&r2}"(b) +; : +; :); +; +; __asm(" lhi %0,10\n" +; " ar %0,%0\n" +; : "={&r2}"(c) +; : +; :); +; } +; +; void f7() { +; int a, b, res; +; +; a = 2147483640; +; b = 10; +; +; __asm(" alr %0,%1\n" +; " jo *-4\n" +; :"=r"(res) +; :"r"(a), "r"(b) +; :); +; } +; +; int f8() { +; +; int a, b, res; +; a = b = res = -1; +; +; __asm(" lhi 1,5\n" +; : +; : +; : "r1"); +; +; __asm(" lgr 2,1\n" +; : +; : +; : "r2"); +; +; __asm(" stg 2,%0\n" +; : +; : "r"(res) +; :); +; +; return res; +; } + +define hidden void @f1() { +; CHECK-LABEL: f1: +; CHECK: *APP +; CHECK-NEXT: lhi 1,5 +; CHECK: *NO_APP +; CHECK: *APP +; CHECK-NEXT: lgr 1,2 +; CHECK: *NO_APP +; CHECK: *APP +; CHECK-NEXT: lgr 0,4 +; CHECK: *NO_APP +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %0 = call i32 asm " lhi $0,5\0A", "={r1}"() + store i32 %0, ptr %a, align 4 + %1 = load i32, ptr %b, align 4 + %2 = call i32 asm " lgr $0,$1\0A", "={r1},{r2}"(i32 %1) + store i32 %2, ptr %a, align 4 + %3 = load i32, ptr %b, align 4 + %4 = call i32 asm " lgr $0,$1\0A", "=r{r1}{r2},{r4}{r5}"(i32 %3) + store i32 %4, ptr %a, align 4 + ret void +} + +define hidden void @f2() { +; CHECK-LABEL: f2: +; CHECK: *APP +; CHECK-NEXT: stg 1,{{.*}}(4,0) +; CHECK: *NO_APP +entry: + %a = alloca i32, align 4 + %m_b = alloca i32, align 4 + %0 = load i32, ptr %a, align 4 + call void asm " stg $1,$0\0A", "=*m,{r1}"(ptr elementtype(i32) %m_b, i32 %0) + ret void +} + +define hidden void @f3() { +; CHECK-LABEL: f3: +; CHECK: l 1,{{.*}}(4) +; CHECK: lhi 15,25 +; CHECK: *APP +; CHECK-NEXT: svc 109 +; CHECK: *NO_APP +entry: + %r15 = alloca i32, align 4 + %r1 = alloca i32, align 4 + %0 = load i32, ptr %r1, align 4 + %1 = call i32 asm " svc 109\0A", "={r15},{r1},{r15}"(i32 %0, i32 25) + store i32 %1, ptr %r15, align 4 + ret void +} + +define hidden void @f4() { +; CHECK-LABEL: f4: +; CHECK: *APP +; CHECK-NEXT: pc 0 +; CHECK: *NO_APP +; CHECK: stg 0,{{.*}}(4) +; CHECK-NEXT: stg 1,{{.*}}(4) +; CHECK-NEXT: stg 15,{{.*}}(4) +entry: + %parm = alloca ptr, align 8 + %rc = alloca i64, align 8 + %reason = alloca i64, align 8 + %code = alloca ptr, align 8 + %0 = load ptr, ptr %parm, align 8 + %1 = load ptr, ptr %code, align 8 + %2 = call { i64, ptr, i64 } asm " pc 0($3)", "={r0},={r1},={r15},r,1"(ptr %1, ptr %0) + %asmresult = extractvalue { i64, ptr, i64 } %2, 0 + %asmresult1 = extractvalue { i64, ptr, i64 } %2, 1 + %asmresult2 = extractvalue { i64, ptr, i64 } %2, 2 + store i64 %asmresult, ptr %reason, align 8 + store ptr %asmresult1, ptr %parm, align 8 + store i64 %asmresult2, ptr %rc, align 8 + ret void +} + +define hidden void @f5() { +; CHECK-LABEL: f5: +; CHECK: *APP +; CHECK-NEXT: lhi {{[0-9]}},10 +; CHECK-NEXT: ar {{[0-9]}},{{[0-9]}} +; CHECK: *NO_APP +; CHECK: *APP +; CHECK-NEXT: lhi 2,10 +; CHECK-NEXT: ar 2,2 +; CHECK: *NO_APP +; CHECK: *APP +; CHECK-NEXT: lhi 2,10 +; CHECK-NEXT: ar 2,2 +; CHECK: *NO_APP +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %0 = call i32 asm " lhi $0,10\0A ar $0,$0\0A", "=&r"() + store i32 %0, ptr %a, align 4 + %1 = call i32 asm " lhi $0,10\0A ar $0,$0\0A", "=&{r2}"() + store i32 %1, ptr %b, align 4 + %2 = call i32 asm " lhi $0,10\0A ar $0,$0\0A", "=&{r2}"() + store i32 %2, ptr %c, align 4 + ret void +} + +define hidden void @f7() { +; CHECK-LABEL: f7: +; CHECK: *APP +; CHECK-NEXT: alr {{[0-9]}},{{[0-9]}} +; CHECK-NEXT: {{.*}}: +; CHECK-NEXT: jo {{.*}}-4 +; CHECK: *NO_APP +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %res = alloca i32, align 4 + store i32 2147483640, ptr %a, align 4 + store i32 10, ptr %b, align 4 + %0 = load i32, ptr %a, align 4 + %1 = load i32, ptr %b, align 4 + %2 = call i32 asm " alr $0,$1\0A jo *-4\0A", "=r,r,r"(i32 %0, i32 %1) + store i32 %2, ptr %res, align 4 + ret void +} + +define hidden signext i32 @f8() { +; CHECK-LABEL: f8: +; CHECK: *APP +; CHECK-NEXT: lhi 1,5 +; CHECK: *NO_APP +; CHECK: *APP +; CHECK-NEXT: lgr 2,1 +; CHECK: *NO_APP +; CHECK: *APP +; CHECK-NEXT: stg 2,{{.*}}(4,0) +; CHECK: *NO_APP +; CHECK: lgf 3,{{.*}}(4) +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %res = alloca i32, align 4 + store i32 -1, ptr %res, align 4 + store i32 -1, ptr %b, align 4 + store i32 -1, ptr %a, align 4 + call void asm sideeffect " lhi 1,5\0A", "~{r1}"() + call void asm sideeffect " lgr 2,1\0A", "~{r2}"() + call void asm " stg 2,$0\0A", "=*m"(ptr elementtype(i32) %res) + %0 = load i32, ptr %res, align 4 + ret i32 %0 +}