| // RUN: if [ %llvmver -ge 11 ]; then %clang++ -std=c++17 -O0 %s -S -emit-llvm -o - %loadClangEnzyme | %lli - ; fi |
| // RUN: if [ %llvmver -ge 11 ]; then %clang++ -std=c++17 -O1 %s -S -emit-llvm -o - %loadClangEnzyme | %lli - ; fi |
| // RUN: if [ %llvmver -ge 11 ]; then %clang++ -std=c++17 -O2 %s -S -emit-llvm -o - %loadClangEnzyme | %lli - ; fi |
| // RUN: if [ %llvmver -ge 11 ]; then %clang++ -std=c++17 -O3 %s -S -emit-llvm -o - %loadClangEnzyme | %lli - ; fi |
| // RUN: if [ %llvmver -ge 12 ]; then %clang++ -std=c++17 -O0 %s -S -emit-llvm -o - %newLoadClangEnzyme | %lli - ; fi |
| // RUN: if [ %llvmver -ge 12 ]; then %clang++ -std=c++17 -O1 %s -S -emit-llvm -o - %newLoadClangEnzyme | %lli - ; fi |
| // RUN: if [ %llvmver -ge 12 ]; then %clang++ -std=c++17 -O2 %s -S -emit-llvm -o - %newLoadClangEnzyme | %lli - ; fi |
| // RUN: if [ %llvmver -ge 12 ]; then %clang++ -std=c++17 -O3 %s -S -emit-llvm -o - %newLoadClangEnzyme | %lli - ; fi |
| |
| #include "../test_utils.h" |
| |
| #include <enzyme/enzyme> |
| |
| float f(float x) { return x * x; } |
| float g(const float& x) { return x * x; } |
| float h(const float* x) { return *x * *x; } |
| |
| static const float x = 10; |
| static const float etalon = 2 * x; |
| |
| void test_active() { |
| { // Ok, reverse mode + active |
| const float dfdx = enzyme::get<0>( |
| enzyme::get<0>( |
| enzyme::autodiff<enzyme::Reverse>( |
| f, enzyme::Active{ x } |
| ) |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon, 1e-10); |
| } |
| } |
| |
| void test_duplicated() { |
| { // Ok, reverse mode + reference arg |
| float dfdx = 0; |
| enzyme::autodiff<enzyme::Reverse>( |
| g, enzyme::Duplicated<const float&>{ x, dfdx } |
| ); |
| APPROX_EQ(dfdx, etalon, 1e-10); |
| } |
| { // Ok, reverse mode + pointer arg |
| float dfdx = 0; |
| enzyme::autodiff<enzyme::Reverse>( |
| h, enzyme::Duplicated<const float*>{ &x, &dfdx } |
| ); |
| APPROX_EQ(dfdx, etalon, 1e-10); |
| } |
| { // OK, forward mode + pass-by-value |
| const float dfdx = enzyme::get<0>( |
| enzyme::autodiff<enzyme::Forward>( |
| f, enzyme::Duplicated<float>{ x, 1.0f } |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon, 1e-10); |
| } |
| { |
| /* |
| * OK: forward mode + pass by reference |
| * Regular __enzyme_fwddiff will not allow a non-pointer here too |
| */ |
| const float dx = 1.0f; |
| const float dfdx = enzyme::get<0>( |
| enzyme::autodiff<enzyme::Forward>( |
| g, enzyme::Duplicated<const float&>{ x, dx } |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon, 1e-10); |
| } |
| { |
| /* |
| * OK: forward mode + pass by pointer |
| * Regular __enzyme_fwddiff will not allow a non-pointer here too |
| */ |
| const float dx = 1.0f; |
| const float dfdx = enzyme::get<0>( |
| enzyme::autodiff<enzyme::Forward>( |
| h, enzyme::Duplicated<const float*>{ &x, &dx } |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon, 1e-10); |
| } |
| |
| #if 0 // Lambdas don't work with the C++ interface yet :( |
| { |
| const auto l = [] (const float& x) { return x * x; }; |
| float dfdx = 0; |
| enzyme::autodiff<enzyme::Reverse>( |
| l, enzyme::Duplicated<const float&>{ x, dfdx } |
| ); |
| APPROX_EQ(dfdx, etalon, 1e-10); |
| } |
| #endif |
| } |
| |
| float f2(float x, float y) { return x * y; } |
| float g2(float x, const float& y) { return x * y; } |
| float h2(float x, float& y) { return x * (y++); } |
| float j2(float x, const float* y) { return x * (*y); } |
| |
| static const float y = 5; |
| static const float etalon2 = 5; |
| |
| void test_const() { |
| { |
| // OK: By-value const for by-value arg |
| const auto dfdx = enzyme::get<0>( |
| enzyme::get<0>( |
| enzyme::autodiff<enzyme::Reverse>( |
| f2, enzyme::Active{ x }, enzyme::Const<float>{ y } |
| ) |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon2, 1e-10); |
| } |
| { |
| // OK: By-const-ref const for by-const-ref arg |
| const auto dfdx = enzyme::get<0>( |
| enzyme::get<0>( |
| enzyme::autodiff<enzyme::Reverse>( |
| g2, enzyme::Active{ x }, enzyme::Const<const float&>{ y } |
| ) |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon2, 1e-10); |
| } |
| { |
| // OK: By-non-const-ref Const for by-non-const-ref arg |
| // fun fact: putting `const T*` instead of `T*` in `enzyme/utils:108` |
| // not only is semantically incorrect, but also makes the compiler |
| // crash on this example! |
| float y2 = y; |
| const auto dfdx = enzyme::get<0>( |
| enzyme::get<0>( |
| enzyme::autodiff<enzyme::Reverse>( |
| h2, enzyme::Active{ x }, enzyme::Const<float&>{ y2 } |
| ) |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon2, 1e-10); |
| APPROX_EQ(y2, y + 1, 1e-10); |
| } |
| { |
| // OK: By-pointer Const for by-pointer arg |
| // I feel like testing with non-const pointer too would be excessive |
| const auto dfdx = enzyme::get<0>( |
| enzyme::get<0>( |
| enzyme::autodiff<enzyme::Reverse>( |
| j2, enzyme::Active{ x }, enzyme::Const<const float*>{ &y } |
| ) |
| ) |
| ); |
| APPROX_EQ(dfdx, etalon2, 1e-10); |
| } |
| } |
| |
| int main() { |
| test_active(); |
| test_duplicated(); |
| test_const(); |
| |
| return 0; |
| } |