r217187 - MS inline asm: Allow __asm blocks to set a return value

Timur Iskhodzhanov timurrrr at google.com
Fri Sep 5 03:05:27 PDT 2014


Looks like r217192 wasn't enough?

2014-09-05 13:41 GMT+04:00 Renato Golin <renato.golin at linaro.org>:
> Hi Reid,
>
> The test is triggering errors on our build, can you restrict it somewhere?
>
> error: MS-style inline assembly is not available: No available targets
> are compatible with this triple, see -version for the available
> targets.
>
> http://lab.llvm.org:8011/builders/clang-native-arm-cortex-a15/builds/2461/steps/check-all/logs/Clang%3A%3Ams-inline-asm-return.cpp
>
> cheers,
> --renato
>
> On 4 September 2014 21:04, Reid Kleckner <reid at kleckner.net> wrote:
>> Author: rnk
>> Date: Thu Sep  4 15:04:38 2014
>> New Revision: 217187
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=217187&view=rev
>> Log:
>> MS inline asm: Allow __asm blocks to set a return value
>>
>> If control falls off the end of a function after an __asm block, MSVC
>> assumes that the inline assembly filled the EAX and possibly EDX
>> registers with an appropriate return value. This functionality is used
>> in inline functions returning 64-bit integers in system headers, so we
>> need some amount of compatibility.
>>
>> This is implemented in Clang by adding extra output constraints to every
>> inline asm block, and storing the resulting output registers into the
>> return value slot. If we see an asm block somewhere in the function
>> body, we emit a normal epilogue instead of marking the end of the
>> function with a return type unreachable.
>>
>> Normal returns in functions not using this functionality will overwrite
>> the return value slot, and in most cases LLVM should be able to
>> eliminate the dead stores.
>>
>> Fixes PR17201.
>>
>> Reviewed By: majnemer
>>
>> Differential Revision: http://reviews.llvm.org/D5177
>>
>> Added:
>>     cfe/trunk/test/CodeGenCXX/ms-inline-asm-return.cpp
>> Modified:
>>     cfe/trunk/lib/CodeGen/CGStmt.cpp
>>     cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
>>     cfe/trunk/lib/CodeGen/CodeGenFunction.h
>>     cfe/trunk/lib/CodeGen/TargetInfo.cpp
>>     cfe/trunk/lib/CodeGen/TargetInfo.h
>>     cfe/trunk/test/CodeGen/ms-inline-asm.c
>>
>> Modified: cfe/trunk/lib/CodeGen/CGStmt.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGStmt.cpp?rev=217187&r1=217186&r2=217187&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGStmt.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGStmt.cpp Thu Sep  4 15:04:38 2014
>> @@ -1901,7 +1901,19 @@ void CodeGenFunction::EmitAsmStmt(const
>>      }
>>    }
>>
>> -  unsigned NumConstraints = S.getNumOutputs() + S.getNumInputs();
>> +  // If this is a Microsoft-style asm blob, store the return registers (EAX:EDX)
>> +  // to the return value slot. Only do this when returning in registers.
>> +  if (isa<MSAsmStmt>(&S)) {
>> +    const ABIArgInfo &RetAI = CurFnInfo->getReturnInfo();
>> +    if (RetAI.isDirect() || RetAI.isExtend()) {
>> +      // Make a fake lvalue for the return value slot.
>> +      LValue ReturnSlot = MakeAddrLValue(ReturnValue, FnRetTy);
>> +      CGM.getTargetCodeGenInfo().addReturnRegisterOutputs(
>> +          *this, ReturnSlot, Constraints, ResultRegTypes, ResultTruncRegTypes,
>> +          ResultRegDests, AsmString, S.getNumOutputs());
>> +      SawAsmBlock = true;
>> +    }
>> +  }
>>
>>    for (unsigned i = 0, e = S.getNumInputs(); i != e; i++) {
>>      const Expr *InputExpr = S.getInputExpr(i);
>> @@ -1974,9 +1986,9 @@ void CodeGenFunction::EmitAsmStmt(const
>>      StringRef Clobber = S.getClobber(i);
>>
>>      if (Clobber != "memory" && Clobber != "cc")
>> -    Clobber = getTarget().getNormalizedGCCRegisterName(Clobber);
>> +      Clobber = getTarget().getNormalizedGCCRegisterName(Clobber);
>>
>> -    if (i != 0 || NumConstraints != 0)
>> +    if (!Constraints.empty())
>>        Constraints += ',';
>>
>>      Constraints += "~{";
>> @@ -2035,6 +2047,9 @@ void CodeGenFunction::EmitAsmStmt(const
>>      }
>>    }
>>
>> +  assert(RegResults.size() == ResultRegTypes.size());
>> +  assert(RegResults.size() == ResultTruncRegTypes.size());
>> +  assert(RegResults.size() == ResultRegDests.size());
>>    for (unsigned i = 0, e = RegResults.size(); i != e; ++i) {
>>      llvm::Value *Tmp = RegResults[i];
>>
>>
>> Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=217187&r1=217186&r2=217187&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Thu Sep  4 15:04:38 2014
>> @@ -39,7 +39,7 @@ CodeGenFunction::CodeGenFunction(CodeGen
>>                CGBuilderInserterTy(this)),
>>        CapturedStmtInfo(nullptr), SanOpts(&CGM.getLangOpts().Sanitize),
>>        IsSanitizerScope(false), CurFuncIsThunk(false), AutoreleaseResult(false),
>> -      BlockInfo(nullptr), BlockPointer(nullptr),
>> +      SawAsmBlock(false), BlockInfo(nullptr), BlockPointer(nullptr),
>>        LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr),
>>        NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr),
>>        ExceptionSlot(nullptr), EHSelectorSlot(nullptr),
>> @@ -878,7 +878,7 @@ void CodeGenFunction::GenerateCode(Globa
>>    // C11 6.9.1p12:
>>    //   If the '}' that terminates a function is reached, and the value of the
>>    //   function call is used by the caller, the behavior is undefined.
>> -  if (getLangOpts().CPlusPlus && !FD->hasImplicitReturnZero() &&
>> +  if (getLangOpts().CPlusPlus && !FD->hasImplicitReturnZero() && !SawAsmBlock &&
>>        !FD->getReturnType()->isVoidType() && Builder.GetInsertBlock()) {
>>      if (SanOpts->Return) {
>>        SanitizerScope SanScope(this);
>>
>> Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=217187&r1=217186&r2=217187&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
>> +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Thu Sep  4 15:04:38 2014
>> @@ -265,6 +265,10 @@ public:
>>    /// In ARC, whether we should autorelease the return value.
>>    bool AutoreleaseResult;
>>
>> +  /// Whether we processed a Microsoft-style asm block during CodeGen. These can
>> +  /// potentially set the return value.
>> +  bool SawAsmBlock;
>> +
>>    const CodeGen::CGBlockInfo *BlockInfo;
>>    llvm::Value *BlockPointer;
>>
>>
>> Modified: cfe/trunk/lib/CodeGen/TargetInfo.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.cpp?rev=217187&r1=217186&r2=217187&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/TargetInfo.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/TargetInfo.cpp Thu Sep  4 15:04:38 2014
>> @@ -15,6 +15,7 @@
>>  #include "TargetInfo.h"
>>  #include "ABIInfo.h"
>>  #include "CGCXXABI.h"
>> +#include "CGValue.h"
>>  #include "CodeGenFunction.h"
>>  #include "clang/AST/RecordLayout.h"
>>  #include "clang/CodeGen/CGFunctionInfo.h"
>> @@ -593,6 +594,14 @@ public:
>>      return X86AdjustInlineAsmType(CGF, Constraint, Ty);
>>    }
>>
>> +  void addReturnRegisterOutputs(CodeGenFunction &CGF, LValue ReturnValue,
>> +                                std::string &Constraints,
>> +                                std::vector<llvm::Type *> &ResultRegTypes,
>> +                                std::vector<llvm::Type *> &ResultTruncRegTypes,
>> +                                std::vector<LValue> &ResultRegDests,
>> +                                std::string &AsmString,
>> +                                unsigned NumOutputs) const override;
>> +
>>    llvm::Constant *
>>    getUBSanFunctionSignature(CodeGen::CodeGenModule &CGM) const override {
>>      unsigned Sig = (0xeb << 0) |  // jmp rel8
>> @@ -606,6 +615,85 @@ public:
>>
>>  }
>>
>> +/// Rewrite input constraint references after adding some output constraints.
>> +/// In the case where there is one output and one input and we add one output,
>> +/// we need to replace all operand references greater than or equal to 1:
>> +///     mov $0, $1
>> +///     mov eax, $1
>> +/// The result will be:
>> +///     mov $0, $2
>> +///     mov eax, $2
>> +static void rewriteInputConstraintReferences(unsigned FirstIn,
>> +                                             unsigned NumNewOuts,
>> +                                             std::string &AsmString) {
>> +  std::string Buf;
>> +  llvm::raw_string_ostream OS(Buf);
>> +  size_t Pos = 0;
>> +  while (Pos < AsmString.size()) {
>> +    size_t DollarStart = AsmString.find('$', Pos);
>> +    if (DollarStart == std::string::npos)
>> +      DollarStart = AsmString.size();
>> +    size_t DollarEnd = AsmString.find_first_not_of('$', DollarStart);
>> +    if (DollarEnd == std::string::npos)
>> +      DollarEnd = AsmString.size();
>> +    OS << StringRef(&AsmString[Pos], DollarEnd - Pos);
>> +    Pos = DollarEnd;
>> +    size_t NumDollars = DollarEnd - DollarStart;
>> +    if (NumDollars % 2 != 0 && Pos < AsmString.size()) {
>> +      // We have an operand reference.
>> +      size_t DigitStart = Pos;
>> +      size_t DigitEnd = AsmString.find_first_not_of("0123456789", DigitStart);
>> +      if (DigitEnd == std::string::npos)
>> +        DigitEnd = AsmString.size();
>> +      StringRef OperandStr(&AsmString[DigitStart], DigitEnd - DigitStart);
>> +      unsigned OperandIndex;
>> +      if (!OperandStr.getAsInteger(10, OperandIndex)) {
>> +        if (OperandIndex >= FirstIn)
>> +          OperandIndex += NumNewOuts;
>> +        OS << OperandIndex;
>> +      } else {
>> +        OS << OperandStr;
>> +      }
>> +      Pos = DigitEnd;
>> +    }
>> +  }
>> +  AsmString = std::move(OS.str());
>> +}
>> +
>> +/// Add output constraints for EAX:EDX because they are return registers.
>> +void X86_32TargetCodeGenInfo::addReturnRegisterOutputs(
>> +    CodeGenFunction &CGF, LValue ReturnSlot, std::string &Constraints,
>> +    std::vector<llvm::Type *> &ResultRegTypes,
>> +    std::vector<llvm::Type *> &ResultTruncRegTypes,
>> +    std::vector<LValue> &ResultRegDests, std::string &AsmString,
>> +    unsigned NumOutputs) const {
>> +  uint64_t RetWidth = CGF.getContext().getTypeSize(ReturnSlot.getType());
>> +
>> +  // Use the EAX constraint if the width is 32 or smaller and EAX:EDX if it is
>> +  // larger.
>> +  if (!Constraints.empty())
>> +    Constraints += ',';
>> +  if (RetWidth <= 32) {
>> +    Constraints += "={eax}";
>> +    ResultRegTypes.push_back(CGF.Int32Ty);
>> +  } else {
>> +    // Use the 'A' constraint for EAX:EDX.
>> +    Constraints += "=A";
>> +    ResultRegTypes.push_back(CGF.Int64Ty);
>> +  }
>> +
>> +  // Truncate EAX or EAX:EDX to an integer of the appropriate size.
>> +  llvm::Type *CoerceTy = llvm::IntegerType::get(CGF.getLLVMContext(), RetWidth);
>> +  ResultTruncRegTypes.push_back(CoerceTy);
>> +
>> +  // Coerce the integer by bitcasting the return slot pointer.
>> +  ReturnSlot.setAddress(CGF.Builder.CreateBitCast(ReturnSlot.getAddress(),
>> +                                                  CoerceTy->getPointerTo()));
>> +  ResultRegDests.push_back(ReturnSlot);
>> +
>> +  rewriteInputConstraintReferences(NumOutputs, 1, AsmString);
>> +}
>> +
>>  /// shouldReturnTypeInRegister - Determine if the given type should be
>>  /// passed in a register (for the Darwin ABI).
>>  bool X86_32ABIInfo::shouldReturnTypeInRegister(QualType Ty,
>>
>> Modified: cfe/trunk/lib/CodeGen/TargetInfo.h
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.h?rev=217187&r1=217186&r2=217187&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/TargetInfo.h (original)
>> +++ cfe/trunk/lib/CodeGen/TargetInfo.h Thu Sep  4 15:04:38 2014
>> @@ -15,6 +15,7 @@
>>  #ifndef LLVM_CLANG_LIB_CODEGEN_TARGETINFO_H
>>  #define LLVM_CLANG_LIB_CODEGEN_TARGETINFO_H
>>
>> +#include "CGValue.h"
>>  #include "clang/AST/Type.h"
>>  #include "clang/Basic/LLVM.h"
>>  #include "llvm/ADT/SmallString.h"
>> @@ -129,6 +130,14 @@ public:
>>      return Ty;
>>    }
>>
>> +  /// Adds constraints and types for result registers.
>> +  virtual void addReturnRegisterOutputs(
>> +      CodeGen::CodeGenFunction &CGF, CodeGen::LValue ReturnValue,
>> +      std::string &Constraints, std::vector<llvm::Type *> &ResultRegTypes,
>> +      std::vector<llvm::Type *> &ResultTruncRegTypes,
>> +      std::vector<CodeGen::LValue> &ResultRegDests, std::string &AsmString,
>> +      unsigned NumOutputs) const {}
>> +
>>    /// doesReturnSlotInterfereWithArgs - Return true if the target uses an
>>    /// argument slot for an 'sret' type.
>>    virtual bool doesReturnSlotInterfereWithArgs() const { return true; }
>>
>> Modified: cfe/trunk/test/CodeGen/ms-inline-asm.c
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/ms-inline-asm.c?rev=217187&r1=217186&r2=217187&view=diff
>> ==============================================================================
>> --- cfe/trunk/test/CodeGen/ms-inline-asm.c (original)
>> +++ cfe/trunk/test/CodeGen/ms-inline-asm.c Thu Sep  4 15:04:38 2014
>> @@ -66,7 +66,7 @@ int t8() {
>>    __asm int 4
>>    return 10;
>>  // CHECK: t8
>> -// CHECK: call void asm sideeffect inteldialect "int $$4\0A\09int $$4", "~{dirflag},~{fpsr},~{flags}"()
>> +// CHECK: call i32 asm sideeffect inteldialect "int $$4\0A\09int $$4", "={eax},~{dirflag},~{fpsr},~{flags}"()
>>  // CHECK: ret i32 10
>>  }
>>
>> @@ -88,10 +88,11 @@ unsigned t10(void) {
>>    }
>>    return j;
>>  // CHECK: t10
>> +// CHECK: [[r:%[a-zA-Z0-9]+]] = alloca i32, align 4
>>  // CHECK: [[I:%[a-zA-Z0-9]+]] = alloca i32, align 4
>>  // CHECK: [[J:%[a-zA-Z0-9]+]] = alloca i32, align 4
>>  // CHECK: store i32 1, i32* [[I]], align 4
>> -// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $1\0A\09mov dword ptr $0, eax", "=*m,*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}, i32* %{{.*}})
>> +// CHECK: call i32 asm sideeffect inteldialect "mov eax, dword ptr $2\0A\09mov dword ptr $0, eax", "=*m,={eax},*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}, i32* %{{.*}})
>>  // CHECK: [[RET:%[a-zA-Z0-9]+]] = load i32* [[J]], align 4
>>  // CHECK: ret i32 [[RET]]
>>  }
>> @@ -112,7 +113,7 @@ unsigned t12(void) {
>>    }
>>    return j + m;
>>  // CHECK: t12
>> -// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $2\0A\09mov dword ptr $0, eax\0A\09mov eax, dword ptr $3\0A\09mov dword ptr $1, eax", "=*m,=*m,*m,*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}, i32* %{{.*}}, i32* %{{.*}}, i32* %{{.*}})
>> +// CHECK: call i32 asm sideeffect inteldialect "mov eax, dword ptr $3\0A\09mov dword ptr $0, eax\0A\09mov eax, dword ptr $4\0A\09mov dword ptr $1, eax", "=*m,=*m,={eax},*m,*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}, i32* %{{.*}}, i32* %{{.*}}, i32* %{{.*}})
>>  }
>>
>>  void t13() {
>> @@ -319,11 +320,11 @@ int *t30()
>>  {
>>    int *res;
>>    __asm lea edi, results
>> -// CHECK: lea edi, dword ptr $1
>> +// CHECK: lea edi, dword ptr $2
>>    __asm mov res, edi
>>  // CHECK: mov dword ptr $0, edi
>>    return res;
>> -// CHECK: "=*m,*m,~{edi},~{dirflag},~{fpsr},~{flags}"(i32** %{{.*}}, [2 x i32]* @{{.*}})
>> +// CHECK: "=*m,={eax},*m,~{edi},~{dirflag},~{fpsr},~{flags}"(i32** %{{.*}}, [2 x i32]* @{{.*}})
>>  }
>>
>>  void t31() {
>>
>> Added: cfe/trunk/test/CodeGenCXX/ms-inline-asm-return.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/ms-inline-asm-return.cpp?rev=217187&view=auto
>> ==============================================================================
>> --- cfe/trunk/test/CodeGenCXX/ms-inline-asm-return.cpp (added)
>> +++ cfe/trunk/test/CodeGenCXX/ms-inline-asm-return.cpp Thu Sep  4 15:04:38 2014
>> @@ -0,0 +1,99 @@
>> +// RUN: %clang_cc1 %s -emit-llvm -o - -fasm-blocks | FileCheck %s
>> +
>> +// Check that we take EAX or EAX:EDX and return it from these functions for MSVC
>> +// compatibility.
>> +
>> +extern "C" {
>> +
>> +long long f_i64() {
>> +  __asm {
>> +    mov eax, 1
>> +    mov edx, 1
>> +  }
>> +}
>> +// CHECK-LABEL: define i64 @f_i64()
>> +// CHECK: %[[r:[^ ]*]] = call i64 asm sideeffect inteldialect "mov eax, $$1\0A\09mov edx, $$1", "=A,~{eax},{{.*}}"
>> +// CHECK: ret i64 %[[r]]
>> +
>> +int f_i32() {
>> +  __asm {
>> +    mov eax, 1
>> +    mov edx, 1
>> +  }
>> +}
>> +// CHECK-LABEL: define i32 @f_i32()
>> +// CHECK: %[[r:[^ ]*]] = call i32 asm sideeffect inteldialect "mov eax, $$1\0A\09mov edx, $$1", "={eax},~{eax},{{.*}}"
>> +// CHECK: ret i32 %[[r]]
>> +
>> +short f_i16() {
>> +  __asm {
>> +    mov eax, 1
>> +    mov edx, 1
>> +  }
>> +}
>> +// CHECK-LABEL: define signext i16 @f_i16()
>> +// CHECK: %[[r:[^ ]*]] = call i32 asm sideeffect inteldialect "mov eax, $$1\0A\09mov edx, $$1", "={eax},~{eax},{{.*}}"
>> +// CHECK: %[[r_i16:[^ ]*]] = trunc i32 %[[r]] to i16
>> +// CHECK: ret i16 %[[r_i16]]
>> +
>> +char f_i8() {
>> +  __asm {
>> +    mov eax, 1
>> +    mov edx, 1
>> +  }
>> +}
>> +// CHECK-LABEL: define signext i8 @f_i8()
>> +// CHECK: %[[r:[^ ]*]] = call i32 asm sideeffect inteldialect "mov eax, $$1\0A\09mov edx, $$1", "={eax},~{eax},{{.*}}"
>> +// CHECK: %[[r_i8:[^ ]*]] = trunc i32 %[[r]] to i8
>> +// CHECK: ret i8 %[[r_i8]]
>> +
>> +bool f_i1() {
>> +  __asm {
>> +    mov eax, 1
>> +    mov edx, 1
>> +  }
>> +}
>> +// CHECK-LABEL: define zeroext i1 @f_i1()
>> +// CHECK: %[[r:[^ ]*]] = call i32 asm sideeffect inteldialect "mov eax, $$1\0A\09mov edx, $$1", "={eax},~{eax},{{.*}}"
>> +// CHECK: %[[r_i8:[^ ]*]] = trunc i32 %[[r]] to i8
>> +// CHECK: store i8 %[[r_i8]], i8* %{{.*}}
>> +// CHECK: %[[r_i1:[^ ]*]] = load i1* %{{.*}}
>> +// CHECK: ret i1 %[[r_i1]]
>> +
>> +struct FourChars {
>> +  char a, b, c, d;
>> +};
>> +FourChars f_s4() {
>> +  __asm {
>> +    mov eax, 0x01010101
>> +  }
>> +}
>> +// CHECK-LABEL: define i32 @f_s4()
>> +// CHECK: %[[r:[^ ]*]] = call i32 asm sideeffect inteldialect "mov eax, $$0x01010101", "={eax},~{eax},{{.*}}"
>> +// CHECK: store i32 %[[r]], i32* %{{.*}}
>> +// CHECK: %[[r_i32:[^ ]*]] = load i32* %{{.*}}
>> +// CHECK: ret i32 %[[r_i32]]
>> +
>> +struct EightChars {
>> +  char a, b, c, d, e, f, g, h;
>> +};
>> +EightChars f_s8() {
>> +  __asm {
>> +    mov eax, 0x01010101
>> +    mov edx, 0x01010101
>> +  }
>> +}
>> +// CHECK-LABEL: define i64 @f_s8()
>> +// CHECK: %[[r:[^ ]*]] = call i64 asm sideeffect inteldialect "mov eax, $$0x01010101\0A\09mov edx, $$0x01010101", "=A,~{eax},{{.*}}"
>> +// CHECK: store i64 %[[r]], i64* %{{.*}}
>> +// CHECK: %[[r_i64:[^ ]*]] = load i64* %{{.*}}
>> +// CHECK: ret i64 %[[r_i64]]
>> +
>> +} // extern "C"
>> +
>> +int main() {
>> +  __asm xor eax, eax
>> +}
>> +// CHECK-LABEL: define i32 @main()
>> +// CHECK: %[[r:[^ ]*]] = call i32 asm sideeffect inteldialect "xor eax, eax", "={eax},{{.*}}"
>> +// CHECK: ret i32 %[[r]]
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits




More information about the cfe-commits mailing list