| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -124,6 +124,7 @@ enum class CudaArch { | |
| GFX1103, | ||
| GFX1150, | ||
| GFX1151, | ||
| GFX1152, | ||
| GFX12_GENERIC, | ||
| GFX1200, | ||
| GFX1201, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| //===------------------------- MemberPointer.cpp ----------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "MemberPointer.h" | ||
| #include "Context.h" | ||
| #include "FunctionPointer.h" | ||
| #include "Program.h" | ||
| #include "Record.h" | ||
|
|
||
| namespace clang { | ||
| namespace interp { | ||
|
|
||
| std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const { | ||
| if (!Dcl || isa<FunctionDecl>(Dcl)) | ||
| return Base; | ||
| const FieldDecl *FD = cast<FieldDecl>(Dcl); | ||
| assert(FD); | ||
|
|
||
| if (!Base.isBlockPointer()) | ||
| return std::nullopt; | ||
|
|
||
| Pointer CastedBase = | ||
| (PtrOffset < 0 ? Base.atField(-PtrOffset) : Base.atFieldSub(PtrOffset)); | ||
|
|
||
| const Record *BaseRecord = CastedBase.getRecord(); | ||
| if (!BaseRecord) | ||
| return std::nullopt; | ||
|
|
||
| assert(BaseRecord); | ||
| if (FD->getParent() == BaseRecord->getDecl()) | ||
| return CastedBase.atField(BaseRecord->getField(FD)->Offset); | ||
|
|
||
| const RecordDecl *FieldParent = FD->getParent(); | ||
| const Record *FieldRecord = Ctx.getRecord(FieldParent); | ||
|
|
||
| unsigned Offset = 0; | ||
| Offset += FieldRecord->getField(FD)->Offset; | ||
| Offset += CastedBase.block()->getDescriptor()->getMetadataSize(); | ||
|
|
||
| if (Offset > CastedBase.block()->getSize()) | ||
| return std::nullopt; | ||
|
|
||
| if (const RecordDecl *BaseDecl = Base.getDeclPtr().getRecord()->getDecl(); | ||
| BaseDecl != FieldParent) | ||
| Offset += Ctx.collectBaseOffset(FieldParent, BaseDecl); | ||
|
|
||
| if (Offset > CastedBase.block()->getSize()) | ||
| return std::nullopt; | ||
|
|
||
| assert(Offset <= CastedBase.block()->getSize()); | ||
| return Pointer(const_cast<Block *>(Base.block()), Offset, Offset); | ||
| } | ||
|
|
||
| FunctionPointer MemberPointer::toFunctionPointer(const Context &Ctx) const { | ||
| return FunctionPointer(Ctx.getProgram().getFunction(cast<FunctionDecl>(Dcl))); | ||
| } | ||
|
|
||
| APValue MemberPointer::toAPValue() const { | ||
| if (isZero()) | ||
| return APValue(static_cast<ValueDecl *>(nullptr), /*IsDerivedMember=*/false, | ||
| /*Path=*/{}); | ||
|
|
||
| if (hasBase()) | ||
| return Base.toAPValue(); | ||
|
|
||
| return APValue(cast<ValueDecl>(getDecl()), /*IsDerivedMember=*/false, | ||
| /*Path=*/{}); | ||
| } | ||
|
|
||
| } // namespace interp | ||
| } // namespace clang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| //===------------------------- MemberPointer.h ------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H | ||
| #define LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H | ||
|
|
||
| #include "Pointer.h" | ||
| #include <optional> | ||
|
|
||
| namespace clang { | ||
| class ASTContext; | ||
| namespace interp { | ||
|
|
||
| class Context; | ||
| class FunctionPointer; | ||
|
|
||
| class MemberPointer final { | ||
| private: | ||
| Pointer Base; | ||
| const Decl *Dcl = nullptr; | ||
| int32_t PtrOffset = 0; | ||
|
|
||
| MemberPointer(Pointer Base, const Decl *Dcl, int32_t PtrOffset) | ||
| : Base(Base), Dcl(Dcl), PtrOffset(PtrOffset) {} | ||
|
|
||
| public: | ||
| MemberPointer() = default; | ||
| MemberPointer(Pointer Base, const Decl *Dcl) : Base(Base), Dcl(Dcl) {} | ||
| MemberPointer(uint32_t Address, const Descriptor *D) { | ||
| // We only reach this for Address == 0, when creating a null member pointer. | ||
| assert(Address == 0); | ||
| } | ||
|
|
||
| MemberPointer(const Decl *D) : Dcl(D) { | ||
| assert((isa<FieldDecl, IndirectFieldDecl, CXXMethodDecl>(D))); | ||
| } | ||
|
|
||
| uint64_t getIntegerRepresentation() const { | ||
| assert( | ||
| false && | ||
| "getIntegerRepresentation() shouldn't be reachable for MemberPointers"); | ||
| return 17; | ||
| } | ||
|
|
||
| std::optional<Pointer> toPointer(const Context &Ctx) const; | ||
|
|
||
| FunctionPointer toFunctionPointer(const Context &Ctx) const; | ||
|
|
||
| Pointer getBase() const { | ||
| if (PtrOffset < 0) | ||
| return Base.atField(-PtrOffset); | ||
| return Base.atFieldSub(PtrOffset); | ||
| } | ||
| bool isMemberFunctionPointer() const { | ||
| return isa_and_nonnull<CXXMethodDecl>(Dcl); | ||
| } | ||
| const CXXMethodDecl *getMemberFunction() const { | ||
| return dyn_cast_if_present<CXXMethodDecl>(Dcl); | ||
| } | ||
| const FieldDecl *getField() const { | ||
| return dyn_cast_if_present<FieldDecl>(Dcl); | ||
| } | ||
|
|
||
| bool hasDecl() const { return Dcl; } | ||
| const Decl *getDecl() const { return Dcl; } | ||
|
|
||
| MemberPointer atInstanceBase(unsigned Offset) const { | ||
| if (Base.isZero()) | ||
| return MemberPointer(Base, Dcl, Offset); | ||
| return MemberPointer(this->Base, Dcl, Offset + PtrOffset); | ||
| } | ||
|
|
||
| MemberPointer takeInstance(Pointer Instance) const { | ||
| assert(this->Base.isZero()); | ||
| return MemberPointer(Instance, this->Dcl, this->PtrOffset); | ||
| } | ||
|
|
||
| APValue toAPValue() const; | ||
|
|
||
| bool isZero() const { return Base.isZero() && !Dcl; } | ||
| bool hasBase() const { return !Base.isZero(); } | ||
|
|
||
| void print(llvm::raw_ostream &OS) const { | ||
| OS << "MemberPtr(" << Base << " " << (void *)Dcl << " + " << PtrOffset | ||
| << ")"; | ||
| } | ||
|
|
||
| std::string toDiagnosticString(const ASTContext &Ctx) const { | ||
| return "FIXME"; | ||
| } | ||
|
|
||
| ComparisonCategoryResult compare(const MemberPointer &RHS) const { | ||
| if (this->Dcl == RHS.Dcl) | ||
| return ComparisonCategoryResult::Equal; | ||
| return ComparisonCategoryResult::Unordered; | ||
| } | ||
| }; | ||
|
|
||
| inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MemberPointer FP) { | ||
| FP.print(OS); | ||
| return OS; | ||
| } | ||
|
|
||
| } // namespace interp | ||
| } // namespace clang | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| // RUN: %clang_cc1 -std=c++14 -fexperimental-new-constant-interpreter -verify=expected,both %s | ||
| // RUN: %clang_cc1 -std=c++14 -verify=ref,both %s | ||
|
|
||
| namespace MemberPointers { | ||
| struct A { | ||
| constexpr A(int n) : n(n) {} | ||
| int n; | ||
| constexpr int f() const { return n + 3; } | ||
| }; | ||
|
|
||
| constexpr A a(7); | ||
| static_assert(A(5).*&A::n == 5, ""); | ||
| static_assert((&a)->*&A::n == 7, ""); | ||
| static_assert((A(8).*&A::f)() == 11, ""); | ||
| static_assert(((&a)->*&A::f)() == 10, ""); | ||
|
|
||
| struct B : A { | ||
| constexpr B(int n, int m) : A(n), m(m) {} | ||
| int m; | ||
| constexpr int g() const { return n + m + 1; } | ||
| }; | ||
| constexpr B b(9, 13); | ||
| static_assert(B(4, 11).*&A::n == 4, ""); | ||
| static_assert(B(4, 11).*&B::m == 11, ""); | ||
| static_assert(B(4, 11).m == 11, ""); | ||
| static_assert(B(4, 11).*(int(A::*))&B::m == 11, ""); | ||
| static_assert(B(4, 11).*&B::m == 11, ""); | ||
| static_assert((&b)->*&A::n == 9, ""); | ||
| static_assert((&b)->*&B::m == 13, ""); | ||
| static_assert((&b)->*(int(A::*))&B::m == 13, ""); | ||
| static_assert((B(4, 11).*&A::f)() == 7, ""); | ||
| static_assert((B(4, 11).*&B::g)() == 16, ""); | ||
|
|
||
| static_assert((B(4, 11).*(int(A::*)() const)&B::g)() == 16, ""); | ||
|
|
||
| static_assert(((&b)->*&A::f)() == 12, ""); | ||
| static_assert(((&b)->*&B::g)() == 23, ""); | ||
| static_assert(((&b)->*(int(A::*)()const)&B::g)() == 23, ""); | ||
|
|
||
|
|
||
| struct S { | ||
| constexpr S(int m, int n, int (S::*pf)() const, int S::*pn) : | ||
| m(m), n(n), pf(pf), pn(pn) {} | ||
| constexpr S() : m(), n(), pf(&S::f), pn(&S::n) {} | ||
|
|
||
| constexpr int f() const { return this->*pn; } | ||
| virtual int g() const; | ||
|
|
||
| int m, n; | ||
| int (S::*pf)() const; | ||
| int S::*pn; | ||
| }; | ||
|
|
||
| constexpr int S::*pm = &S::m; | ||
| constexpr int S::*pn = &S::n; | ||
|
|
||
| constexpr int (S::*pf)() const = &S::f; | ||
| constexpr int (S::*pg)() const = &S::g; | ||
|
|
||
| constexpr S s(2, 5, &S::f, &S::m); | ||
|
|
||
| static_assert((s.*&S::f)() == 2, ""); | ||
| static_assert((s.*s.pf)() == 2, ""); | ||
|
|
||
| static_assert(pf == &S::f, ""); | ||
|
|
||
| static_assert(pf == s.*&S::pf, ""); | ||
|
|
||
| static_assert(pm == &S::m, ""); | ||
| static_assert(pm != pn, ""); | ||
| static_assert(s.pn != pn, ""); | ||
| static_assert(s.pn == pm, ""); | ||
| static_assert(pg != nullptr, ""); | ||
| static_assert(pf != nullptr, ""); | ||
| static_assert((int S::*)nullptr == nullptr, ""); | ||
| static_assert(pg == pg, ""); // both-error {{constant expression}} \ | ||
| // both-note {{comparison of pointer to virtual member function 'g' has unspecified value}} | ||
| static_assert(pf != pg, ""); // both-error {{constant expression}} \ | ||
| // both-note {{comparison of pointer to virtual member function 'g' has unspecified value}} | ||
|
|
||
| template<int n> struct T : T<n-1> { const int X = n;}; | ||
| template<> struct T<0> { int n; char k;}; | ||
| template<> struct T<30> : T<29> { int m; }; | ||
|
|
||
| T<17> t17; | ||
| T<30> t30; | ||
|
|
||
| constexpr int (T<15>::*deepm) = (int(T<10>::*))&T<30>::m; | ||
| constexpr int (T<10>::*deepn) = &T<0>::n; | ||
| constexpr char (T<10>::*deepk) = &T<0>::k; | ||
|
|
||
| static_assert(&(t17.*deepn) == &t17.n, ""); | ||
| static_assert(&(t17.*deepk) == &t17.k, ""); | ||
| static_assert(deepn == &T<2>::n, ""); | ||
|
|
||
| constexpr int *pgood = &(t30.*deepm); | ||
| constexpr int *pbad = &(t17.*deepm); // both-error {{constant expression}} | ||
| static_assert(&(t30.*deepm) == &t30.m, ""); | ||
|
|
||
| static_assert(deepm == &T<50>::m, ""); | ||
| static_assert(deepm != deepn, ""); | ||
|
|
||
| constexpr T<5> *p17_5 = &t17; | ||
| constexpr T<13> *p17_13 = (T<13>*)p17_5; | ||
| constexpr T<23> *p17_23 = (T<23>*)p17_13; // both-error {{constant expression}} \ | ||
| // both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<23>'}} | ||
| constexpr T<18> *p17_18 = (T<18>*)p17_13; // both-error {{constant expression}} \ | ||
| // both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<18>'}} | ||
| static_assert(&(p17_5->*(int(T<0>::*))deepn) == &t17.n, ""); | ||
| static_assert(&(p17_5->*(int(T<0>::*))deepn), ""); | ||
|
|
||
|
|
||
| static_assert(&(p17_13->*deepn) == &t17.n, ""); | ||
| constexpr int *pbad2 = &(p17_13->*(int(T<9>::*))deepm); // both-error {{constant expression}} | ||
|
|
||
| constexpr T<5> *p30_5 = &t30; | ||
| constexpr T<23> *p30_23 = (T<23>*)p30_5; | ||
| constexpr T<13> *p30_13 = p30_23; | ||
| static_assert(&(p30_13->*deepn) == &t30.n, ""); | ||
| static_assert(&(p30_23->*deepn) == &t30.n, ""); | ||
| static_assert(&(p30_5->*(int(T<3>::*))deepn) == &t30.n, ""); | ||
|
|
||
| static_assert(&(p30_5->*(int(T<2>::*))deepm) == &t30.m, ""); | ||
| static_assert(&(((T<17>*)p30_13)->*deepm) == &t30.m, ""); | ||
| static_assert(&(p30_23->*deepm) == &t30.m, ""); | ||
|
|
||
|
|
||
| /// Added tests not from constant-expression-cxx11.cpp | ||
| static_assert(pm, ""); | ||
| static_assert(!((int S::*)nullptr), ""); | ||
| constexpr int S::*pk = nullptr; | ||
| static_assert(!pk, ""); | ||
| } | ||
|
|
||
| namespace test3 { | ||
| struct nsCSSRect { | ||
| }; | ||
| static int nsCSSRect::* sides; | ||
| nsCSSRect dimenX; | ||
| void ParseBoxCornerRadii(int y) { | ||
| switch (y) { | ||
| } | ||
| int& x = dimenX.*sides; | ||
| } | ||
| } | ||
|
|
||
| void foo() { | ||
| class X; | ||
| void (X::*d) (); | ||
| d = nullptr; /// This calls in the constant interpreter. | ||
| } | ||
|
|
||
| namespace { | ||
| struct A { int n; }; | ||
| struct B { int n; }; | ||
| struct C : A, B {}; | ||
| struct D { double d; C c; }; | ||
| const int &&u = static_cast<B&&>(0, ((D&&)D{}).*&D::c).n; // both-warning {{left operand of comma operator has no effect}} | ||
| } | ||
|
|
||
| /// From SemaTemplate/instantiate-member-pointers.cpp | ||
| namespace { | ||
| struct Y { | ||
| int x; | ||
| }; | ||
|
|
||
| template<typename T, typename Class, T Class::*Ptr> | ||
| struct X3 { | ||
| X3<T, Class, Ptr> &operator=(const T& value) { | ||
| return *this; | ||
| } | ||
| }; | ||
|
|
||
| typedef int Y::*IntMember; | ||
| template<IntMember Member> | ||
| struct X4 { | ||
| X3<int, Y, Member> member; | ||
| int &getMember(Y& y) { return y.*Member; } | ||
| }; | ||
|
|
||
| int &get_X4(X4<&Y::x> x4, Y& y) { | ||
| return x4.getMember(y); | ||
| } | ||
| } | ||
|
|
||
| /// From test/CXX/basic/basic.def.odr/p2.cpp | ||
| namespace { | ||
| void use(int); | ||
| struct S { int x; int f() const; }; | ||
| constexpr S *ps = nullptr; | ||
| S *const &psr = ps; | ||
|
|
||
| void test() { | ||
| use(ps->*&S::x); | ||
| use(psr->*&S::x); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature | ||
| // REQUIRES: webassembly-registered-target | ||
|
|
||
| // Simple calls to known variadic functions that are completely elided when | ||
| // optimisations are on This is a functional check that the expand-variadic pass | ||
| // is consistent with clang's va_arg handling | ||
|
|
||
| // When expand-variadics is added to the default pipeline, clang -O1 will | ||
| // suffice here -Wno-varargs avoids warning second argument to 'va_start' is not | ||
| // the last named parameter | ||
|
|
||
| // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -Wno-varargs -O1 -emit-llvm -o - | opt - -S --passes='module(expand-variadics,default<O1>)' --expand-variadics-override=optimize -o - | FileCheck %s | ||
|
|
||
| #include <stdarg.h> | ||
| #include <stdint.h> | ||
|
|
||
| template <typename X, typename Y> static X first(...) { | ||
| va_list va; | ||
| __builtin_va_start(va, 0); | ||
| X r = va_arg(va, X); | ||
| va_end(va); | ||
| return r; | ||
| } | ||
|
|
||
| template <typename X, typename Y> static Y second(...) { | ||
| va_list va; | ||
| __builtin_va_start(va, 0); | ||
| va_arg(va, X); | ||
| Y r = va_arg(va, Y); | ||
| va_end(va); | ||
| return r; | ||
| } | ||
|
|
||
| extern "C" { | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_pair_i32 | ||
| // CHECK-SAME: (i32 noundef returned [[X:%.*]], i32 noundef [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[X]] | ||
| // | ||
| int first_pair_i32(int x, int y) { return first<int, int>(x, y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_pair_i32 | ||
| // CHECK-SAME: (i32 noundef [[X:%.*]], i32 noundef returned [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[Y]] | ||
| // | ||
| int second_pair_i32(int x, int y) { return second<int, int>(x, y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_pair_f64 | ||
| // CHECK-SAME: (double noundef returned [[X:%.*]], double noundef [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret double [[X]] | ||
| // | ||
| double first_pair_f64(double x, double y) { | ||
| return first<double, double>(x, y); | ||
| } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_pair_f64 | ||
| // CHECK-SAME: (double noundef [[X:%.*]], double noundef returned [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret double [[Y]] | ||
| // | ||
| double second_pair_f64(double x, double y) { | ||
| return second<double, double>(x, y); | ||
| } | ||
| } | ||
|
|
||
| extern "C" { | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_i32_f64 | ||
| // CHECK-SAME: (i32 noundef returned [[X:%.*]], double noundef [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[X]] | ||
| // | ||
| int first_i32_f64(int x, double y) { return first<int, double>(x, y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_i32_f64 | ||
| // CHECK-SAME: (i32 noundef [[X:%.*]], double noundef returned [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret double [[Y]] | ||
| // | ||
| double second_i32_f64(int x, double y) { return second<int, double>(x, y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_f64_i32 | ||
| // CHECK-SAME: (double noundef returned [[X:%.*]], i32 noundef [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret double [[X]] | ||
| // | ||
| double first_f64_i32(double x, int y) { return first<double, int>(x, y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_f64_i32 | ||
| // CHECK-SAME: (double noundef [[X:%.*]], i32 noundef returned [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[Y]] | ||
| // | ||
| int second_f64_i32(double x, int y) { return second<double, int>(x, y); } | ||
| } | ||
|
|
||
| extern "C" { | ||
| typedef uint64_t ulong2 __attribute__((__vector_size__(16), __aligned__(16))); | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_i32_ulong2 | ||
| // CHECK-SAME: (i32 noundef returned [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[X]] | ||
| // | ||
| int first_i32_ulong2(int x, ulong2 *y) { return first<int, ulong2>(x, *y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_i32_ulong2 | ||
| // CHECK-SAME: (i32 noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]], ptr nocapture noundef writeonly [[R:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: [[TMP0:%.*]] = load <2 x i64>, ptr [[Y]], align 16, !tbaa [[TBAA2:![0-9]+]] | ||
| // CHECK-NEXT: store <2 x i64> [[TMP0]], ptr [[R]], align 16, !tbaa [[TBAA2]] | ||
| // CHECK-NEXT: ret void | ||
| // | ||
| void second_i32_ulong2(int x, ulong2 *y, ulong2 *r) { | ||
| *r = second<int, ulong2>(x, *y); | ||
| } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_ulong2_i32 | ||
| // CHECK-SAME: (ptr nocapture noundef readonly [[X:%.*]], i32 noundef [[Y:%.*]], ptr nocapture noundef writeonly [[R:%.*]]) local_unnamed_addr #[[ATTR1]] { | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: [[TMP0:%.*]] = load <2 x i64>, ptr [[X]], align 16, !tbaa [[TBAA2]] | ||
| // CHECK-NEXT: store <2 x i64> [[TMP0]], ptr [[R]], align 16, !tbaa [[TBAA2]] | ||
| // CHECK-NEXT: ret void | ||
| // | ||
| void first_ulong2_i32(ulong2 *x, int y, ulong2 *r) { | ||
| *r = first<ulong2, int>(*x, y); | ||
| } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_ulong2_i32 | ||
| // CHECK-SAME: (ptr nocapture noundef readonly [[X:%.*]], i32 noundef returned [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[Y]] | ||
| // | ||
| int second_ulong2_i32(ulong2 *x, int y) { return second<ulong2, int>(*x, y); } | ||
| } | ||
|
|
||
| // ascending alignment | ||
| typedef struct { | ||
| char c; | ||
| short s; | ||
| int i; | ||
| long l; | ||
| float f; | ||
| double d; | ||
| } asc; | ||
|
|
||
| extern "C" { | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_i32_asc | ||
| // CHECK-SAME: (i32 noundef returned [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[X]] | ||
| // | ||
| int first_i32_asc(int x, asc *y) { return first<int, asc>(x, *y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_i32_asc | ||
| // CHECK-SAME: (i32 noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]], ptr nocapture noundef writeonly [[R:%.*]]) local_unnamed_addr #[[ATTR1]] { | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i32(ptr noundef nonnull align 8 dereferenceable(24) [[R]], ptr noundef nonnull align 1 dereferenceable(24) [[Y]], i32 24, i1 false) | ||
| // CHECK-NEXT: ret void | ||
| // | ||
| void second_i32_asc(int x, asc *y, asc *r) { *r = second<int, asc>(x, *y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@first_asc_i32 | ||
| // CHECK-SAME: (ptr nocapture noundef readonly [[X:%.*]], i32 noundef [[Y:%.*]], ptr nocapture noundef writeonly [[R:%.*]]) local_unnamed_addr #[[ATTR1]] { | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i32(ptr noundef nonnull align 8 dereferenceable(24) [[R]], ptr noundef nonnull align 1 dereferenceable(24) [[X]], i32 24, i1 false) | ||
| // CHECK-NEXT: ret void | ||
| // | ||
| void first_asc_i32(asc *x, int y, asc *r) { *r = first<asc, int>(*x, y); } | ||
|
|
||
| // CHECK-LABEL: define {{[^@]+}}@second_asc_i32 | ||
| // CHECK-SAME: (ptr nocapture noundef readonly [[X:%.*]], i32 noundef returned [[Y:%.*]]) | ||
| // CHECK-NEXT: entry: | ||
| // CHECK-NEXT: ret i32 [[Y]] | ||
| // | ||
| int second_asc_i32(asc *x, int y) { return second<asc, int>(*x, y); } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // RUN: %clang -target aarch64 -mcpu=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=Phoenix %s | ||
| // RUN: %clang -target aarch64 -mlittle-endian -mcpu=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=Phoenix %s | ||
| // RUN: %clang -target aarch64_be -mlittle-endian -mcpu=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=Phoenix %s | ||
| // RUN: %clang -target aarch64 -mtune=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=Phoenix-TUNE %s | ||
| // RUN: %clang -target aarch64 -mlittle-endian -mtune=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=Phoenix-TUNE %s | ||
| // RUN: %clang -target aarch64_be -mlittle-endian -mtune=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=Phoenix-TUNE %s | ||
| // Phoenix: "-cc1"{{.*}} "-triple" "aarch64{{(--)?}}"{{.*}} "-target-cpu" "oryon-1" "-target-feature" "+v8.6a" | ||
| // Phoenix-TUNE: "-cc1"{{.*}} "-triple" "aarch64{{(--)?}}"{{.*}} "-target-cpu" "generic" | ||
|
|
||
| // RUN: %clang -target arm64 -mcpu=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-Phoenix %s | ||
| // RUN: %clang -target arm64 -mlittle-endian -mcpu=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-Phoenix %s | ||
| // RUN: %clang -target arm64 -mtune=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-Phoenix-TUNE %s | ||
| // RUN: %clang -target arm64 -mlittle-endian -mtune=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-Phoenix-TUNE %s | ||
| // ARM64-Phoenix: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "oryon-1" "-target-feature" "+v8.6a" | ||
| // ARM64-Phoenix-TUNE: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "generic" | ||
|
|
||
| // RUN: %clang -target aarch64 -mcpu=oryon-1 -mtune=cortex-a53 -### -c %s 2>&1 | FileCheck -check-prefix=MCPU-MTUNE-Phoenix %s | ||
| // RUN: %clang -target aarch64 -mtune=cortex-a53 -mcpu=oryon-1 -### -c %s 2>&1 | FileCheck -check-prefix=MCPU-MTUNE-Phoenix %s | ||
| // MCPU-MTUNE-Phoenix: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-cpu" "oryon-1" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // REQUIRES: host-supports-jit | ||
| // UNSUPPORTED: system-aix | ||
| // RUN: cat %s | clang-repl -Xcc -xc | FileCheck %s | ||
| // RUN: cat %s | clang-repl -Xcc -std=c++11 | FileCheck %s | ||
|
|
||
| // Fails with `Symbols not found: [ __clang_Interpreter_SetValueNoAlloc ]`. | ||
| // UNSUPPORTED: hwasan | ||
|
|
||
| const char* c_str = "Hello, world!"; c_str | ||
|
|
||
| // CHECK: Not implement yet. |