| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| //===----- SemaOpenACC.h - Semantic Analysis for OpenACC constructs -------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// \file | ||
| /// This file declares semantic analysis for OpenACC constructs and | ||
| /// clauses. | ||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_SEMA_SEMAOPENACC_H | ||
| #define LLVM_CLANG_SEMA_SEMAOPENACC_H | ||
|
|
||
| #include "clang/AST/DeclGroup.h" | ||
| #include "clang/Basic/OpenACCKinds.h" | ||
| #include "clang/Basic/SourceLocation.h" | ||
| #include "clang/Sema/Ownership.h" | ||
|
|
||
| namespace clang { | ||
|
|
||
| class ASTContext; | ||
| class DiagnosticEngine; | ||
| class LangOptions; | ||
| class Sema; | ||
|
|
||
| class SemaOpenACC { | ||
| public: | ||
| SemaOpenACC(Sema &S); | ||
|
|
||
| ASTContext &getASTContext() const; | ||
| DiagnosticsEngine &getDiagnostics() const; | ||
| const LangOptions &getLangOpts() const; | ||
|
|
||
| Sema &SemaRef; | ||
|
|
||
| /// Called after parsing an OpenACC Clause so that it can be checked. | ||
| bool ActOnClause(OpenACCClauseKind ClauseKind, SourceLocation StartLoc); | ||
|
|
||
| /// Called after the construct has been parsed, but clauses haven't been | ||
| /// parsed. This allows us to diagnose not-implemented, as well as set up any | ||
| /// state required for parsing the clauses. | ||
| void ActOnConstruct(OpenACCDirectiveKind K, SourceLocation StartLoc); | ||
|
|
||
| /// Called after the directive, including its clauses, have been parsed and | ||
| /// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES | ||
| /// happen before any associated declarations or statements have been parsed. | ||
| /// This function is only called when we are parsing a 'statement' context. | ||
| bool ActOnStartStmtDirective(OpenACCDirectiveKind K, SourceLocation StartLoc); | ||
|
|
||
| /// Called after the directive, including its clauses, have been parsed and | ||
| /// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES | ||
| /// happen before any associated declarations or statements have been parsed. | ||
| /// This function is only called when we are parsing a 'Decl' context. | ||
| bool ActOnStartDeclDirective(OpenACCDirectiveKind K, SourceLocation StartLoc); | ||
| /// Called when we encounter an associated statement for our construct, this | ||
| /// should check legality of the statement as it appertains to this Construct. | ||
| StmtResult ActOnAssociatedStmt(OpenACCDirectiveKind K, StmtResult AssocStmt); | ||
|
|
||
| /// Called after the directive has been completely parsed, including the | ||
| /// declaration group or associated statement. | ||
| StmtResult ActOnEndStmtDirective(OpenACCDirectiveKind K, | ||
| SourceLocation StartLoc, | ||
| SourceLocation EndLoc, StmtResult AssocStmt); | ||
| /// Called after the directive has been completely parsed, including the | ||
| /// declaration group or associated statement. | ||
| DeclGroupRef ActOnEndDeclDirective(); | ||
| }; | ||
|
|
||
| } // namespace clang | ||
|
|
||
| #endif // LLVM_CLANG_SEMA_SEMAOPENACC_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // RUN: %clang_cc1 -verify -std=c99 %s | ||
| // RUN: %clang_cc1 -E -std=c99 %s | FileCheck %s | ||
| // expected-no-diagnostics | ||
|
|
||
| /* WG14 N570: Yes | ||
| * Empty macro arguments | ||
| * | ||
| * NB: the original paper is not available online anywhere, so the test | ||
| * coverage is coming from what could be gleaned from the C99 rationale | ||
| * document. In C89, it was UB to pass no arguments to a function-like macro, | ||
| * and that's now supported in C99. | ||
| */ | ||
|
|
||
| #define TEN 10 | ||
| #define U u | ||
| #define I // expands into no preprocessing tokens | ||
| #define L L | ||
| #define glue(a, b) a ## b | ||
| #define xglue(a, b) glue(a, b) | ||
|
|
||
| const unsigned u = xglue(TEN, U); | ||
| const int i = xglue(TEN, I); | ||
| const long l = xglue(TEN, L); | ||
|
|
||
| // CHECK: const unsigned u = 10u; | ||
| // CHECK-NEXT: const int i = 10; | ||
| // CHECK-NEXT: const long l = 10L; | ||
|
|
||
| _Static_assert(u == 10U, ""); | ||
| _Static_assert(i == 10, ""); | ||
| _Static_assert(l == 10L, ""); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s | ||
|
|
||
| void fn(float x[2]) { } | ||
|
|
||
| // CHECK-LABEL: define void {{.*}}call{{.*}} | ||
| // CHECK: [[Arr:%.*]] = alloca [2 x float] | ||
| // CHECK: [[Tmp:%.*]] = alloca [2 x float] | ||
| // CHECK: call void @llvm.memset.p0.i32(ptr align 4 [[Arr]], i8 0, i32 8, i1 false) | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 8, i1 false) | ||
| // CHECK: call void {{.*}}fn{{.*}}(ptr noundef byval([2 x float]) align 4 [[Tmp]]) | ||
| void call() { | ||
| float Arr[2] = {0, 0}; | ||
| fn(Arr); | ||
| } | ||
|
|
||
| struct Obj { | ||
| float V; | ||
| int X; | ||
| }; | ||
|
|
||
| void fn2(Obj O[4]) { } | ||
|
|
||
| // CHECK-LABEL: define void {{.*}}call2{{.*}} | ||
| // CHECK: [[Arr:%.*]] = alloca [4 x %struct.Obj] | ||
| // CHECK: [[Tmp:%.*]] = alloca [4 x %struct.Obj] | ||
| // CHECK: call void @llvm.memset.p0.i32(ptr align 4 [[Arr]], i8 0, i32 32, i1 false) | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 32, i1 false) | ||
| // CHECK: call void {{.*}}fn2{{.*}}(ptr noundef byval([4 x %struct.Obj]) align 4 [[Tmp]]) | ||
| void call2() { | ||
| Obj Arr[4] = {}; | ||
| fn2(Arr); | ||
| } | ||
|
|
||
|
|
||
| void fn3(float x[2][2]) { } | ||
|
|
||
| // CHECK-LABEL: define void {{.*}}call3{{.*}} | ||
| // CHECK: [[Arr:%.*]] = alloca [2 x [2 x float]] | ||
| // CHECK: [[Tmp:%.*]] = alloca [2 x [2 x float]] | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{.*}}, i32 16, i1 false) | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 16, i1 false) | ||
| // CHECK: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x [2 x float]]) align 4 [[Tmp]]) | ||
| void call3() { | ||
| float Arr[2][2] = {{0, 0}, {1,1}}; | ||
| fn3(Arr); | ||
| } | ||
|
|
||
| // CHECK-LABEL: define void {{.*}}call4{{.*}}(ptr | ||
| // CHECK-SAME: noundef byval([2 x [2 x float]]) align 4 [[Arr:%.*]]) | ||
| // CHECK: [[Tmp:%.*]] = alloca [2 x [2 x float]] | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 16, i1 false) | ||
| // CHECK: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x [2 x float]]) align 4 [[Tmp]]) | ||
|
|
||
| void call4(float Arr[2][2]) { | ||
| fn3(Arr); | ||
| } | ||
|
|
||
| // Verify that each template instantiation codegens to a unique and correctly | ||
| // mangled function name. | ||
|
|
||
| // CHECK-LABEL: define void {{.*}}template_call{{.*}}(ptr | ||
|
|
||
| // CHECK-SAME: noundef byval([2 x float]) align 4 [[FA2:%[0-9A-Z]+]], | ||
| // CHECK-SAME: ptr noundef byval([4 x float]) align 4 [[FA4:%[0-9A-Z]+]], | ||
| // CHECK-SAME: ptr noundef byval([3 x i32]) align 4 [[IA3:%[0-9A-Z]+]] | ||
|
|
||
| // CHECK: [[Tmp1:%.*]] = alloca [2 x float] | ||
| // CHECK: [[Tmp2:%.*]] = alloca [4 x float] | ||
| // CHECK: [[Tmp3:%.*]] = alloca [3 x i32] | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp1]], ptr align 4 [[FA2]], i32 8, i1 false) | ||
| // CHECK: call void @"??$template_fn@$$BY01M@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 [[Tmp1]]) | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp2]], ptr align 4 [[FA4]], i32 16, i1 false) | ||
| // CHECK: call void @"??$template_fn@$$BY03M@@YAXY03M@Z"(ptr noundef byval([4 x float]) align 4 [[Tmp2]]) | ||
| // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp3]], ptr align 4 [[IA3]], i32 12, i1 false) | ||
| // CHECK: call void @"??$template_fn@$$BY02H@@YAXY02H@Z"(ptr noundef byval([3 x i32]) align 4 [[Tmp3]]) | ||
|
|
||
| template<typename T> | ||
| void template_fn(T Val) {} | ||
|
|
||
| void template_call(float FA2[2], float FA4[4], int IA3[3]) { | ||
| template_fn(FA2); | ||
| template_fn(FA4); | ||
| template_fn(IA3); | ||
| } | ||
|
|
||
|
|
||
| // Verify that Array parameter element access correctly codegens. | ||
| // CHECK-LABEL: define void {{.*}}element_access{{.*}}(ptr | ||
| // CHECK-SAME: noundef byval([2 x float]) align 4 [[FA2:%[0-9A-Z]+]] | ||
|
|
||
| // CHECK: [[Addr:%.*]] = getelementptr inbounds [2 x float], ptr [[FA2]], i32 0, i32 0 | ||
| // CHECK: [[Tmp:%.*]] = load float, ptr [[Addr]] | ||
| // CHECK: call void @"??$template_fn@M@@YAXM@Z"(float noundef [[Tmp]]) | ||
|
|
||
| // CHECK: [[Idx0:%.*]] = getelementptr inbounds [2 x float], ptr [[FA2]], i32 0, i32 0 | ||
| // CHECK: [[Val0:%.*]] = load float, ptr [[Idx0]] | ||
| // CHECK: [[Sum:%.*]] = fadd float [[Val0]], 5.000000e+00 | ||
| // CHECK: [[Idx1:%.*]] = getelementptr inbounds [2 x float], ptr [[FA2]], i32 0, i32 1 | ||
| // CHECK: store float [[Sum]], ptr [[Idx1]] | ||
|
|
||
| void element_access(float FA2[2]) { | ||
| template_fn(FA2[0]); | ||
| FA2[1] = FA2[0] + 5; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -verify | ||
|
|
||
| void fn(int I[5]); // #fn | ||
| void fn2(int I[3][3]); // #fn2 | ||
|
|
||
| void call() { | ||
| float F[5]; | ||
| double D[4]; | ||
| int Long[9]; | ||
| int Short[4]; | ||
| int Same[5]; | ||
|
|
||
| fn(F); // expected-error{{no matching function for call to 'fn'}} | ||
| // expected-note@#fn{{candidate function not viable: no known conversion from 'float[5]' to 'int[5]' for 1st argument}} | ||
|
|
||
| fn(D); // expected-error{{no matching function for call to 'fn'}} | ||
| // expected-note@#fn{{candidate function not viable: no known conversion from 'double[4]' to 'int[5]' for 1st argument}} | ||
|
|
||
| fn(Long); // expected-error{{no matching function for call to 'fn'}} | ||
| // expected-note@#fn{{candidate function not viable: no known conversion from 'int[9]' to 'int[5]' for 1st argument}} | ||
|
|
||
| fn(Short); // expected-error{{no matching function for call to 'fn'}} | ||
| // expected-note@#fn{{candidate function not viable: no known conversion from 'int[4]' to 'int[5]' for 1st argument}} | ||
|
|
||
| fn(Same); // totally fine, nothing to see here. | ||
|
|
||
| fn2(Long); // expected-error{{no matching function for call to 'fn2'}} | ||
| // expected-note@#fn2{{candidate function not viable: no known conversion from 'int[9]' to 'int[3][3]' for 1st argument}} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump %s | FileCheck %s | ||
|
|
||
| void fn(float x[2]) { } | ||
|
|
||
| // CHECK: CallExpr {{.*}} 'void' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'fn' 'void (float[2])' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <HLSLArrayRValue> | ||
|
|
||
| void call() { | ||
| float Arr[2] = {0, 0}; | ||
| fn(Arr); | ||
| } | ||
|
|
||
| struct Obj { | ||
| float V; | ||
| int X; | ||
| }; | ||
|
|
||
| void fn2(Obj O[4]) { } | ||
|
|
||
| // CHECK: CallExpr {{.*}} 'void' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(Obj[4])' <FunctionToPointerDecay> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'void (Obj[4])' lvalue Function {{.*}} 'fn2' 'void (Obj[4])' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'Obj[4]' <HLSLArrayRValue> | ||
|
|
||
| void call2() { | ||
| Obj Arr[4] = {}; | ||
| fn2(Arr); | ||
| } | ||
|
|
||
|
|
||
| void fn3(float x[2][2]) { } | ||
|
|
||
| // CHECK: CallExpr {{.*}} 'void' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2][2])' <FunctionToPointerDecay> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2][2])' lvalue Function {{.*}} 'fn3' 'void (float[2][2])' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2][2]' <HLSLArrayRValue> | ||
|
|
||
| void call3() { | ||
| float Arr[2][2] = {{0, 0}, {1,1}}; | ||
| fn3(Arr); | ||
| } | ||
|
|
||
| // This template function should be instantiated 3 times for the different array | ||
| // types and lengths. | ||
|
|
||
| // CHECK: FunctionTemplateDecl {{.*}} template_fn | ||
| // CHECK-NEXT: TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T | ||
| // CHECK-NEXT: FunctionDecl {{.*}} template_fn 'void (T)' | ||
| // CHECK-NEXT: ParmVarDecl {{.*}} Val 'T' | ||
|
|
||
| // CHECK: FunctionDecl {{.*}} used template_fn 'void (float[2])' implicit_instantiation | ||
| // CHECK-NEXT: TemplateArgument type 'float[2]' | ||
| // CHECK-NEXT: ArrayParameterType {{.*}} 'float[2]' 2 | ||
| // CHECK-NEXT: BuiltinType {{.*}} 'float' | ||
| // CHECK-NEXT: ParmVarDecl {{.*}} Val 'float[2]' | ||
|
|
||
| // CHECK: FunctionDecl {{.*}} used template_fn 'void (float[4])' implicit_instantiation | ||
| // CHECK-NEXT: TemplateArgument type 'float[4]' | ||
| // CHECK-NEXT: ArrayParameterType {{.*}} 'float[4]' 4 | ||
| // CHECK-NEXT: BuiltinType {{.*}} 'float' | ||
| // CHECK-NEXT: ParmVarDecl {{.*}} Val 'float[4]' | ||
|
|
||
| // CHECK: FunctionDecl {{.*}} used template_fn 'void (int[3])' implicit_instantiation | ||
| // CHECK-NEXT: TemplateArgument type 'int[3]' | ||
| // CHECK-NEXT: ArrayParameterType {{.*}} 'int[3]' 3 | ||
| // CHECK-NEXT: BuiltinType {{.*}} 'int' | ||
| // CHECK-NEXT: ParmVarDecl {{.*}} Val 'int[3]' | ||
|
|
||
| template<typename T> | ||
| void template_fn(T Val) {} | ||
|
|
||
| // CHECK: FunctionDecl {{.*}} call 'void (float[2], float[4], int[3])' | ||
| // CHECK: CallExpr {{.*}} 'void' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'template_fn' 'void (float[2])' (FunctionTemplate {{.*}} 'template_fn') | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <LValueToRValue> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'float[2]' lvalue ParmVar {{.*}} 'FA2' 'float[2]' | ||
| // CHECK-NEXT: CallExpr {{.*}} 'void' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[4])' <FunctionToPointerDecay> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[4])' lvalue Function {{.*}} 'template_fn' 'void (float[4])' (FunctionTemplate {{.*}} 'template_fn') | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[4]' <LValueToRValue> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'float[4]' lvalue ParmVar {{.*}} 'FA4' 'float[4]' | ||
| // CHECK-NEXT: CallExpr {{.*}} 'void' | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int[3])' <FunctionToPointerDecay> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'void (int[3])' lvalue Function {{.*}} 'template_fn' 'void (int[3])' (FunctionTemplate {{.*}} 'template_fn') | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int[3]' <LValueToRValue> | ||
| // CHECK-NEXT: DeclRefExpr {{.*}} 'int[3]' lvalue ParmVar {{.*}} 'IA3' 'int[3]' | ||
|
|
||
| void call(float FA2[2], float FA4[4], int IA3[3]) { | ||
| template_fn(FA2); | ||
| template_fn(FA4); | ||
| template_fn(IA3); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| ; ModuleID = '/Users/cbieneman/dev/llvm-project/clang/test/SemaHLSL/ArrayTemporary.hlsl' | ||
| source_filename = "/Users/cbieneman/dev/llvm-project/clang/test/SemaHLSL/ArrayTemporary.hlsl" | ||
| target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" | ||
| target triple = "dxil-pc-shadermodel6.3-library" | ||
|
|
||
| %struct.Obj = type { float, i32 } | ||
|
|
||
| @"__const.?call3@@YAXXZ.Arr" = private unnamed_addr constant [2 x [2 x float]] [[2 x float] zeroinitializer, [2 x float] [float 1.000000e+00, float 1.000000e+00]], align 4 | ||
|
|
||
| ; Function Attrs: noinline nounwind optnone | ||
| define void @"?fn@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 %x) #0 { | ||
| entry: | ||
| ret void | ||
| } | ||
|
|
||
| ; Function Attrs: noinline nounwind optnone | ||
| define void @"?call@@YAXXZ"() #0 { | ||
| entry: | ||
| %Arr = alloca [2 x float], align 4 | ||
| %agg.tmp = alloca [2 x float], align 4 | ||
| call void @llvm.memset.p0.i32(ptr align 4 %Arr, i8 0, i32 8, i1 false) | ||
| call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %Arr, i32 8, i1 false) | ||
| call void @"?fn@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 %agg.tmp) | ||
| ret void | ||
| } | ||
|
|
||
| ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) | ||
| declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #1 | ||
|
|
||
| ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) | ||
| declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #2 | ||
|
|
||
| ; Function Attrs: noinline nounwind optnone | ||
| define void @"?fn2@@YAXY03UObj@@@Z"(ptr noundef byval([4 x %struct.Obj]) align 4 %O) #0 { | ||
| entry: | ||
| ret void | ||
| } | ||
|
|
||
| ; Function Attrs: noinline nounwind optnone | ||
| define void @"?call2@@YAXXZ"() #0 { | ||
| entry: | ||
| %Arr = alloca [4 x %struct.Obj], align 4 | ||
| %agg.tmp = alloca [4 x %struct.Obj], align 4 | ||
| call void @llvm.memset.p0.i32(ptr align 4 %Arr, i8 0, i32 32, i1 false) | ||
| call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %Arr, i32 32, i1 false) | ||
| call void @"?fn2@@YAXY03UObj@@@Z"(ptr noundef byval([4 x %struct.Obj]) align 4 %agg.tmp) | ||
| ret void | ||
| } | ||
|
|
||
| ; Function Attrs: noinline nounwind optnone | ||
| define void @"?fn3@@YAXY111M@Z"(ptr noundef byval([2 x [2 x float]]) align 4 %x) #0 { | ||
| entry: | ||
| ret void | ||
| } | ||
|
|
||
| ; Function Attrs: noinline nounwind optnone | ||
| define void @"?call3@@YAXXZ"() #0 { | ||
| entry: | ||
| %Arr = alloca [2 x [2 x float]], align 4 | ||
| %agg.tmp = alloca [2 x [2 x float]], align 4 | ||
| call void @llvm.memcpy.p0.p0.i32(ptr align 4 %Arr, ptr align 4 @"__const.?call3@@YAXXZ.Arr", i32 16, i1 false) | ||
| call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %Arr, i32 16, i1 false) | ||
| call void @"?fn3@@YAXY111M@Z"(ptr noundef byval([2 x [2 x float]]) align 4 %agg.tmp) | ||
| ret void | ||
| } | ||
|
|
||
| attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" } | ||
| attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } | ||
| attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } | ||
|
|
||
| !llvm.module.flags = !{!0, !1} | ||
| !llvm.ident = !{!2} | ||
|
|
||
| !0 = !{i32 1, !"wchar_size", i32 4} | ||
| !1 = !{i32 4, !"dx.disable_optimizations", i32 1} | ||
| !2 = !{!"clang version 19.0.0git (git@github.com:llvm/llvm-project.git 64e1c15c520cf11114ef2ddd887e76560903db2b)"} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,306 @@ | ||
| //===-- Single-precision atan2f function ----------------------------------===// | ||
| // | ||
| // 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 "src/math/atan2f.h" | ||
| #include "inv_trigf_utils.h" | ||
| #include "src/__support/FPUtil/FPBits.h" | ||
| #include "src/__support/FPUtil/PolyEval.h" | ||
| #include "src/__support/FPUtil/double_double.h" | ||
| #include "src/__support/FPUtil/multiply_add.h" | ||
| #include "src/__support/FPUtil/nearest_integer.h" | ||
| #include "src/__support/FPUtil/rounding_mode.h" | ||
| #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| namespace { | ||
|
|
||
| // Look up tables for accurate pass: | ||
|
|
||
| // atan(i/16) with i = 0..16, generated by Sollya with: | ||
| // > for i from 0 to 16 do { | ||
| // a = round(atan(i/16), D, RN); | ||
| // b = round(atan(i/16) - a, D, RN); | ||
| // print("{", b, ",", a, "},"); | ||
| // }; | ||
| constexpr fputil::DoubleDouble ATAN_I[17] = { | ||
| {0.0, 0.0}, | ||
| {-0x1.c934d86d23f1dp-60, 0x1.ff55bb72cfdeap-5}, | ||
| {-0x1.cd37686760c17p-59, 0x1.fd5ba9aac2f6ep-4}, | ||
| {0x1.347b0b4f881cap-58, 0x1.7b97b4bce5b02p-3}, | ||
| {0x1.8ab6e3cf7afbdp-57, 0x1.f5b75f92c80ddp-3}, | ||
| {-0x1.963a544b672d8p-57, 0x1.362773707ebccp-2}, | ||
| {-0x1.c63aae6f6e918p-56, 0x1.6f61941e4def1p-2}, | ||
| {-0x1.24dec1b50b7ffp-56, 0x1.a64eec3cc23fdp-2}, | ||
| {0x1.a2b7f222f65e2p-56, 0x1.dac670561bb4fp-2}, | ||
| {-0x1.d5b495f6349e6p-56, 0x1.0657e94db30dp-1}, | ||
| {-0x1.928df287a668fp-58, 0x1.1e00babdefeb4p-1}, | ||
| {0x1.1021137c71102p-55, 0x1.345f01cce37bbp-1}, | ||
| {0x1.2419a87f2a458p-56, 0x1.4978fa3269ee1p-1}, | ||
| {0x1.0028e4bc5e7cap-57, 0x1.5d58987169b18p-1}, | ||
| {-0x1.8c34d25aadef6p-56, 0x1.700a7c5784634p-1}, | ||
| {-0x1.bf76229d3b917p-56, 0x1.819d0b7158a4dp-1}, | ||
| {0x1.1a62633145c07p-55, 0x1.921fb54442d18p-1}, | ||
| }; | ||
|
|
||
| // Taylor polynomial, generated by Sollya with: | ||
| // > for i from 0 to 8 do { | ||
| // j = (-1)^(i + 1)/(2*i + 1); | ||
| // a = round(j, D, RN); | ||
| // b = round(j - a, D, RN); | ||
| // print("{", b, ",", a, "},"); | ||
| // }; | ||
| constexpr fputil::DoubleDouble COEFFS[9] = { | ||
| {0.0, 1.0}, // 1 | ||
| {-0x1.5555555555555p-56, -0x1.5555555555555p-2}, // -1/3 | ||
| {-0x1.999999999999ap-57, 0x1.999999999999ap-3}, // 1/5 | ||
| {-0x1.2492492492492p-57, -0x1.2492492492492p-3}, // -1/7 | ||
| {0x1.c71c71c71c71cp-58, 0x1.c71c71c71c71cp-4}, // 1/9 | ||
| {0x1.745d1745d1746p-59, -0x1.745d1745d1746p-4}, // -1/11 | ||
| {-0x1.3b13b13b13b14p-58, 0x1.3b13b13b13b14p-4}, // 1/13 | ||
| {-0x1.1111111111111p-60, -0x1.1111111111111p-4}, // -1/15 | ||
| {0x1.e1e1e1e1e1e1ep-61, 0x1.e1e1e1e1e1e1ep-5}, // 1/17 | ||
| }; | ||
|
|
||
| // Veltkamp's splitting of a double precision into hi + lo, where the hi part is | ||
| // slightly smaller than an even split, so that the product of | ||
| // hi * (s1 * k + s2) is exact, | ||
| // where: | ||
| // s1, s2 are single precsion, | ||
| // 1/16 <= s1/s2 <= 1 | ||
| // 1/16 <= k <= 1 is an integer. | ||
| // So the maximal precision of (s1 * k + s2) is: | ||
| // prec(s1 * k + s2) = 2 + log2(msb(s2)) - log2(lsb(k_d * s1)) | ||
| // = 2 + log2(msb(s1)) + 4 - log2(lsb(k_d)) - log2(lsb(s1)) | ||
| // = 2 + log2(lsb(s1)) + 23 + 4 - (-4) - log2(lsb(s1)) | ||
| // = 33. | ||
| // Thus, the Veltkamp splitting constant is C = 2^33 + 1. | ||
| // This is used when FMA instruction is not available. | ||
| [[maybe_unused]] constexpr fputil::DoubleDouble split_d(double a) { | ||
| fputil::DoubleDouble r{0.0, 0.0}; | ||
| constexpr double C = 0x1.0p33 + 1.0; | ||
| double t1 = C * a; | ||
| double t2 = a - t1; | ||
| r.hi = t1 + t2; | ||
| r.lo = a - r.hi; | ||
| return r; | ||
| } | ||
|
|
||
| // Compute atan( num_d / den_d ) in double-double precision. | ||
| // num_d = min(|x|, |y|) | ||
| // den_d = max(|x|, |y|) | ||
| // q_d = num_d / den_d | ||
| // idx, k_d = round( 2^4 * num_d / den_d ) | ||
| // final_sign = sign of the final result | ||
| // const_term = the constant term in the final expression. | ||
| float atan2f_double_double(double num_d, double den_d, double q_d, int idx, | ||
| double k_d, double final_sign, | ||
| const fputil::DoubleDouble &const_term) { | ||
| fputil::DoubleDouble q; | ||
| double num_r, den_r; | ||
|
|
||
| if (idx != 0) { | ||
| // The following range reduction is accurate even without fma for | ||
| // 1/16 <= n/d <= 1. | ||
| // atan(n/d) - atan(idx/16) = atan((n/d - idx/16) / (1 + (n/d) * (idx/16))) | ||
| // = atan((n - d*(idx/16)) / (d + n*idx/16)) | ||
| k_d *= 0x1.0p-4; | ||
| num_r = fputil::multiply_add(k_d, -den_d, num_d); // Exact | ||
| den_r = fputil::multiply_add(k_d, num_d, den_d); // Exact | ||
| q.hi = num_r / den_r; | ||
| } else { | ||
| // For 0 < n/d < 1/16, we just need to calculate the lower part of their | ||
| // quotient. | ||
| q.hi = q_d; | ||
| num_r = num_d; | ||
| den_r = den_d; | ||
| } | ||
| #ifdef LIBC_TARGET_CPU_HAS_FMA | ||
| q.lo = fputil::multiply_add(q.hi, -den_r, num_r) / den_r; | ||
| #else | ||
| // Compute `(num_r - q.hi * den_r) / den_r` accurately without FMA | ||
| // instructions. | ||
| fputil::DoubleDouble q_hi_dd = split_d(q.hi); | ||
| double t1 = fputil::multiply_add(q_hi_dd.hi, -den_r, num_r); // Exact | ||
| double t2 = fputil::multiply_add(q_hi_dd.lo, -den_r, t1); | ||
| q.lo = t2 / den_r; | ||
| #endif // LIBC_TARGET_CPU_HAS_FMA | ||
|
|
||
| // Taylor polynomial, evaluating using Horner's scheme: | ||
| // P = x - x^3/3 + x^5/5 -x^7/7 + x^9/9 - x^11/11 + x^13/13 - x^15/15 | ||
| // + x^17/17 | ||
| // = x*(1 + x^2*(-1/3 + x^2*(1/5 + x^2*(-1/7 + x^2*(1/9 + x^2* | ||
| // *(-1/11 + x^2*(1/13 + x^2*(-1/15 + x^2 * 1/17)))))))) | ||
| fputil::DoubleDouble q2 = fputil::quick_mult(q, q); | ||
| fputil::DoubleDouble p_dd = | ||
| fputil::polyeval(q2, COEFFS[0], COEFFS[1], COEFFS[2], COEFFS[3], | ||
| COEFFS[4], COEFFS[5], COEFFS[6], COEFFS[7], COEFFS[8]); | ||
| fputil::DoubleDouble r_dd = | ||
| fputil::add(const_term, fputil::multiply_add(q, p_dd, ATAN_I[idx])); | ||
| r_dd.hi *= final_sign; | ||
| r_dd.lo *= final_sign; | ||
|
|
||
| // Make sure the sum is normalized: | ||
| fputil::DoubleDouble rr = fputil::exact_add(r_dd.hi, r_dd.lo); | ||
| // Round to odd. | ||
| uint64_t rr_bits = cpp::bit_cast<uint64_t>(rr.hi); | ||
| if (LIBC_UNLIKELY(((rr_bits & 0xfff'ffff) == 0) && (rr.lo != 0.0))) { | ||
| Sign hi_sign = fputil::FPBits<double>(rr.hi).sign(); | ||
| Sign lo_sign = fputil::FPBits<double>(rr.lo).sign(); | ||
| if (hi_sign == lo_sign) { | ||
| ++rr_bits; | ||
| } else if ((rr_bits & fputil::FPBits<double>::FRACTION_MASK) > 0) { | ||
| --rr_bits; | ||
| } | ||
| } | ||
|
|
||
| return static_cast<float>(cpp::bit_cast<double>(rr_bits)); | ||
| } | ||
|
|
||
| } // anonymous namespace | ||
|
|
||
| // There are several range reduction steps we can take for atan2(y, x) as | ||
| // follow: | ||
|
|
||
| // * Range reduction 1: signness | ||
| // atan2(y, x) will return a number between -PI and PI representing the angle | ||
| // forming by the 0x axis and the vector (x, y) on the 0xy-plane. | ||
| // In particular, we have that: | ||
| // atan2(y, x) = atan( y/x ) if x >= 0 and y >= 0 (I-quadrant) | ||
| // = pi + atan( y/x ) if x < 0 and y >= 0 (II-quadrant) | ||
| // = -pi + atan( y/x ) if x < 0 and y < 0 (III-quadrant) | ||
| // = atan( y/x ) if x >= 0 and y < 0 (IV-quadrant) | ||
| // Since atan function is odd, we can use the formula: | ||
| // atan(-u) = -atan(u) | ||
| // to adjust the above conditions a bit further: | ||
| // atan2(y, x) = atan( |y|/|x| ) if x >= 0 and y >= 0 (I-quadrant) | ||
| // = pi - atan( |y|/|x| ) if x < 0 and y >= 0 (II-quadrant) | ||
| // = -pi + atan( |y|/|x| ) if x < 0 and y < 0 (III-quadrant) | ||
| // = -atan( |y|/|x| ) if x >= 0 and y < 0 (IV-quadrant) | ||
| // Which can be simplified to: | ||
| // atan2(y, x) = sign(y) * atan( |y|/|x| ) if x >= 0 | ||
| // = sign(y) * (pi - atan( |y|/|x| )) if x < 0 | ||
|
|
||
| // * Range reduction 2: reciprocal | ||
| // Now that the argument inside atan is positive, we can use the formula: | ||
| // atan(1/x) = pi/2 - atan(x) | ||
| // to make the argument inside atan <= 1 as follow: | ||
| // atan2(y, x) = sign(y) * atan( |y|/|x|) if 0 <= |y| <= x | ||
| // = sign(y) * (pi/2 - atan( |x|/|y| ) if 0 <= x < |y| | ||
| // = sign(y) * (pi - atan( |y|/|x| )) if 0 <= |y| <= -x | ||
| // = sign(y) * (pi/2 + atan( |x|/|y| )) if 0 <= -x < |y| | ||
|
|
||
| // * Range reduction 3: look up table. | ||
| // After the previous two range reduction steps, we reduce the problem to | ||
| // compute atan(u) with 0 <= u <= 1, or to be precise: | ||
| // atan( n / d ) where n = min(|x|, |y|) and d = max(|x|, |y|). | ||
| // An accurate polynomial approximation for the whole [0, 1] input range will | ||
| // require a very large degree. To make it more efficient, we reduce the input | ||
| // range further by finding an integer idx such that: | ||
| // | n/d - idx/16 | <= 1/32. | ||
| // In particular, | ||
| // idx := 2^-4 * round(2^4 * n/d) | ||
| // Then for the fast pass, we find a polynomial approximation for: | ||
| // atan( n/d ) ~ atan( idx/16 ) + (n/d - idx/16) * Q(n/d - idx/16) | ||
| // For the accurate pass, we use the addition formula: | ||
| // atan( n/d ) - atan( idx/16 ) = atan( (n/d - idx/16)/(1 + (n*idx)/(16*d)) ) | ||
| // = atan( (n - d * idx/16)/(d + n * idx/16) ) | ||
| // And finally we use Taylor polynomial to compute the RHS in the accurate pass: | ||
| // atan(u) ~ P(u) = u - u^3/3 + u^5/5 - u^7/7 + u^9/9 - u^11/11 + u^13/13 - | ||
| // - u^15/15 + u^17/17 | ||
| // It's error in double-double precision is estimated in Sollya to be: | ||
| // > P = x - x^3/3 + x^5/5 -x^7/7 + x^9/9 - x^11/11 + x^13/13 - x^15/15 | ||
| // + x^17/17; | ||
| // > dirtyinfnorm(atan(x) - P, [-2^-5, 2^-5]); | ||
| // 0x1.aec6f...p-100 | ||
| // which is about rounding errors of double-double (2^-104). | ||
|
|
||
| LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) { | ||
| using FPBits = typename fputil::FPBits<float>; | ||
| constexpr double IS_NEG[2] = {1.0, -1.0}; | ||
| constexpr double PI = 0x1.921fb54442d18p1; | ||
| constexpr double PI_LO = 0x1.1a62633145c07p-53; | ||
| constexpr double PI_OVER_4 = 0x1.921fb54442d18p-1; | ||
| constexpr double PI_OVER_2 = 0x1.921fb54442d18p0; | ||
| constexpr double THREE_PI_OVER_4 = 0x1.2d97c7f3321d2p+1; | ||
| // Adjustment for constant term: | ||
| // CONST_ADJ[x_sign][y_sign][recip] | ||
| constexpr fputil::DoubleDouble CONST_ADJ[2][2][2] = { | ||
| {{{0.0, 0.0}, {-PI_LO / 2, -PI_OVER_2}}, | ||
| {{-0.0, -0.0}, {-PI_LO / 2, -PI_OVER_2}}}, | ||
| {{{-PI_LO, -PI}, {PI_LO / 2, PI_OVER_2}}, | ||
| {{-PI_LO, -PI}, {PI_LO / 2, PI_OVER_2}}}}; | ||
|
|
||
| FPBits x_bits(x), y_bits(y); | ||
| bool x_sign = x_bits.sign().is_neg(); | ||
| bool y_sign = y_bits.sign().is_neg(); | ||
| x_bits.set_sign(Sign::POS); | ||
| y_bits.set_sign(Sign::POS); | ||
| uint32_t x_abs = x_bits.uintval(); | ||
| uint32_t y_abs = y_bits.uintval(); | ||
| uint32_t max_abs = x_abs > y_abs ? x_abs : y_abs; | ||
| uint32_t min_abs = x_abs <= y_abs ? x_abs : y_abs; | ||
|
|
||
| if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || min_abs == 0U)) { | ||
| if (x_bits.is_nan() || y_bits.is_nan()) | ||
| return FPBits::quiet_nan().get_val(); | ||
| size_t x_except = x_abs == 0 ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1); | ||
| size_t y_except = y_abs == 0 ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1); | ||
|
|
||
| // Exceptional cases: | ||
| // EXCEPT[y_except][x_except][x_is_neg] | ||
| // with x_except & y_except: | ||
| // 0: zero | ||
| // 1: finite, non-zero | ||
| // 2: infinity | ||
| constexpr double EXCEPTS[3][3][2] = { | ||
| {{0.0, PI}, {0.0, PI}, {0.0, PI}}, | ||
| {{PI_OVER_2, PI_OVER_2}, {0.0, 0.0}, {0.0, PI}}, | ||
| {{PI_OVER_2, PI_OVER_2}, | ||
| {PI_OVER_2, PI_OVER_2}, | ||
| {PI_OVER_4, THREE_PI_OVER_4}}, | ||
| }; | ||
|
|
||
| double r = IS_NEG[y_sign] * EXCEPTS[y_except][x_except][x_sign]; | ||
|
|
||
| return static_cast<float>(r); | ||
| } | ||
|
|
||
| bool recip = x_abs < y_abs; | ||
| double final_sign = IS_NEG[(x_sign != y_sign) != recip]; | ||
| fputil::DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip]; | ||
| double num_d = static_cast<double>(FPBits(min_abs).get_val()); | ||
| double den_d = static_cast<double>(FPBits(max_abs).get_val()); | ||
| double q_d = num_d / den_d; | ||
|
|
||
| double k_d = fputil::nearest_integer(q_d * 0x1.0p4f); | ||
| int idx = static_cast<int>(k_d); | ||
| q_d = fputil::multiply_add(k_d, -0x1.0p-4, q_d); | ||
|
|
||
| double p = atan_eval(q_d, idx); | ||
| double r = final_sign * | ||
| fputil::multiply_add(q_d, p, const_term.hi + ATAN_COEFFS[idx][0]); | ||
|
|
||
| constexpr uint32_t LOWER_ERR = 4; | ||
| // Mask sticky bits in double precision before rounding to single precision. | ||
| constexpr uint32_t MASK = | ||
| mask_trailing_ones<uint32_t, fputil::FPBits<double>::SIG_LEN - | ||
| FPBits::SIG_LEN - 1>(); | ||
| constexpr uint32_t UPPER_ERR = MASK - LOWER_ERR; | ||
|
|
||
| uint32_t r_bits = static_cast<uint32_t>(cpp::bit_cast<uint64_t>(r)) & MASK; | ||
|
|
||
| // Ziv's rounding test. | ||
| if (LIBC_LIKELY(r_bits > LOWER_ERR && r_bits < UPPER_ERR)) | ||
| return static_cast<float>(r); | ||
|
|
||
| return atan2f_double_double(num_d, den_d, q_d, idx, k_d, final_sign, | ||
| const_term); | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| //===-- Implementation header of fseeko -------------------------*- 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_LIBC_SRC_STDIO_FSEEKO_H | ||
| #define LLVM_LIBC_SRC_STDIO_FSEEKO_H | ||
|
|
||
| #include <stdio.h> | ||
| #include <unistd.h> | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| int fseeko(::FILE *stream, off_t offset, int whence); | ||
|
|
||
| } // namespace LIBC_NAMESPACE | ||
|
|
||
| #endif // LLVM_LIBC_SRC_STDIO_FSEEKO_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| //===-- Implementation header of ftello -------------------------*- 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_LIBC_SRC_STDIO_FTELLO_H | ||
| #define LLVM_LIBC_SRC_STDIO_FTELLO_H | ||
|
|
||
| #include <stdio.h> | ||
| #include <unistd.h> | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| off_t ftello(::FILE *f); | ||
|
|
||
| } // namespace LIBC_NAMESPACE | ||
|
|
||
| #endif // LLVM_LIBC_SRC_STDIO_FTELLO_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,6 @@ | |
| #include "src/__support/File/file.h" | ||
|
|
||
| #include "src/errno/libc_errno.h" | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| //===-- Implementation of fseeko ------------------------------------------===// | ||
| // | ||
| // 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 "src/stdio/fseeko.h" | ||
| #include "src/__support/File/file.h" | ||
|
|
||
| #include "src/errno/libc_errno.h" | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| LLVM_LIBC_FUNCTION(int, fseeko, (::FILE * stream, off_t offset, int whence)) { | ||
| auto result = | ||
| reinterpret_cast<LIBC_NAMESPACE::File *>(stream)->seek(offset, whence); | ||
| if (!result.has_value()) { | ||
| libc_errno = result.error(); | ||
| return -1; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,6 @@ | |
| #include "src/__support/File/file.h" | ||
|
|
||
| #include "src/errno/libc_errno.h" | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| //===-- Implementation of ftello ------------------------------------------===// | ||
| // | ||
| // 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 "src/stdio/ftello.h" | ||
| #include "src/__support/File/file.h" | ||
|
|
||
| #include "src/errno/libc_errno.h" | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| LLVM_LIBC_FUNCTION(off_t, ftello, (::FILE * stream)) { | ||
| auto result = reinterpret_cast<LIBC_NAMESPACE::File *>(stream)->tell(); | ||
| if (!result.has_value()) { | ||
| libc_errno = result.error(); | ||
| return -1; | ||
| } | ||
| return result.value(); | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| //===-- Unittests for atan2f ----------------------------------------------===// | ||
| // | ||
| // 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 "include/llvm-libc-macros/math-macros.h" | ||
| #include "src/__support/FPUtil/FPBits.h" | ||
| #include "src/math/atan2f.h" | ||
| #include "test/UnitTest/FPMatcher.h" | ||
| #include "test/UnitTest/Test.h" | ||
| #include "utils/MPFRWrapper/MPFRUtils.h" | ||
|
|
||
| using LlvmLibcAtan2fTest = LIBC_NAMESPACE::testing::FPTest<float>; | ||
| using LIBC_NAMESPACE::testing::tlog; | ||
|
|
||
| namespace mpfr = LIBC_NAMESPACE::testing::mpfr; | ||
|
|
||
| TEST_F(LlvmLibcAtan2fTest, TrickyInputs) { | ||
| constexpr int N = 17; | ||
| mpfr::BinaryInput<float> INPUTS[N] = { | ||
| {0x1.0cb3a4p+20f, 0x1.4ebacp+22f}, {0x1.12215p+1f, 0x1.4fabfcp+22f}, | ||
| {-0x1.13baaep+41f, 0x1.5bd22ep+23f}, {0x1.1ff7dcp+41f, 0x1.aec0a6p+23f}, | ||
| {0x1.2bc794p+23f, 0x1.0bc0c6p+23f}, {0x1.2fba3ap+42f, 0x1.f99456p+23f}, | ||
| {0x1.5ea1f8p+27f, 0x1.f2a1aep+23f}, {0x1.7a931p+44f, 0x1.352ac4p+22f}, | ||
| {0x1.8802bcp+21f, 0x1.8f130ap+23f}, {0x1.658ef8p+17f, 0x1.3c00f4p+22f}, | ||
| {0x1.69fb0cp+21f, 0x1.39e4c4p+23f}, {0x1.8eb24cp+11f, 0x1.36518p+23f}, | ||
| {0x1.9e7ebp+30f, 0x1.d80522p+23f}, {0x1.b4bdeep+19f, 0x1.c19b4p+23f}, | ||
| {0x1.bc201p+43f, 0x1.617346p+23f}, {0x1.c96c3cp+20f, 0x1.c01d1ep+23f}, | ||
| {0x1.781fcp+28f, 0x1.dcb3cap+23f}, | ||
| }; | ||
|
|
||
| for (int i = 0; i < N; ++i) { | ||
| float x = INPUTS[i].x; | ||
| float y = INPUTS[i].y; | ||
| ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i], | ||
| LIBC_NAMESPACE::atan2f(x, y), 0.5); | ||
| INPUTS[i].x = -INPUTS[i].x; | ||
| ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i], | ||
| LIBC_NAMESPACE::atan2f(-x, y), 0.5); | ||
| INPUTS[i].y = -INPUTS[i].y; | ||
| ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i], | ||
| LIBC_NAMESPACE::atan2f(-x, -y), 0.5); | ||
| INPUTS[i].x = -INPUTS[i].x; | ||
| ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i], | ||
| LIBC_NAMESPACE::atan2f(x, -y), 0.5); | ||
| } | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcAtan2fTest, InFloatRange) { | ||
| constexpr uint32_t X_COUNT = 1'23; | ||
| constexpr uint32_t X_START = FPBits(0.25f).uintval(); | ||
| constexpr uint32_t X_STOP = FPBits(4.0f).uintval(); | ||
| constexpr uint32_t X_STEP = (X_STOP - X_START) / X_COUNT; | ||
|
|
||
| constexpr uint32_t Y_COUNT = 1'37; | ||
| constexpr uint32_t Y_START = FPBits(0.25f).uintval(); | ||
| constexpr uint32_t Y_STOP = FPBits(4.0f).uintval(); | ||
| constexpr uint32_t Y_STEP = (Y_STOP - Y_START) / Y_COUNT; | ||
|
|
||
| auto test = [&](mpfr::RoundingMode rounding_mode) { | ||
| mpfr::ForceRoundingMode __r(rounding_mode); | ||
| if (!__r.success) | ||
| return; | ||
|
|
||
| uint64_t fails = 0; | ||
| uint64_t finite_count = 0; | ||
| uint64_t total_count = 0; | ||
| float failed_x, failed_y, failed_r = 0.0; | ||
| double tol = 0.5; | ||
|
|
||
| for (uint32_t i = 0, v = X_START; i <= X_COUNT; ++i, v += X_STEP) { | ||
| float x = FPBits(v).get_val(); | ||
| if (isnan(x) || isinf(x) || x < 0.0) | ||
| continue; | ||
|
|
||
| for (uint32_t j = 0, w = Y_START; j <= Y_COUNT; ++j, w += Y_STEP) { | ||
| float y = FPBits(w).get_val(); | ||
| if (isnan(y) || isinf(y)) | ||
| continue; | ||
|
|
||
| LIBC_NAMESPACE::libc_errno = 0; | ||
| float result = LIBC_NAMESPACE::atan2f(x, y); | ||
| ++total_count; | ||
| if (isnan(result) || isinf(result)) | ||
| continue; | ||
|
|
||
| ++finite_count; | ||
| mpfr::BinaryInput<float> inputs{x, y}; | ||
|
|
||
| if (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Atan2, inputs, | ||
| result, 0.5, rounding_mode)) { | ||
| ++fails; | ||
| while (!TEST_MPFR_MATCH_ROUNDING_SILENTLY( | ||
| mpfr::Operation::Atan2, inputs, result, tol, rounding_mode)) { | ||
| failed_x = x; | ||
| failed_y = y; | ||
| failed_r = result; | ||
|
|
||
| if (tol > 1000.0) | ||
| break; | ||
|
|
||
| tol *= 2.0; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if (fails || (finite_count < total_count)) { | ||
| tlog << " Atan2f failed: " << fails << "/" << finite_count << "/" | ||
| << total_count << " tests.\n" | ||
| << " Max ULPs is at most: " << static_cast<uint64_t>(tol) << ".\n"; | ||
| } | ||
| if (fails) { | ||
| mpfr::BinaryInput<float> inputs{failed_x, failed_y}; | ||
| EXPECT_MPFR_MATCH(mpfr::Operation::Atan2, inputs, failed_r, 0.5, | ||
| rounding_mode); | ||
| } | ||
| }; | ||
|
|
||
| tlog << " Test Rounding To Nearest...\n"; | ||
| test(mpfr::RoundingMode::Nearest); | ||
|
|
||
| tlog << " Test Rounding Downward...\n"; | ||
| test(mpfr::RoundingMode::Downward); | ||
|
|
||
| tlog << " Test Rounding Upward...\n"; | ||
| test(mpfr::RoundingMode::Upward); | ||
|
|
||
| tlog << " Test Rounding Toward Zero...\n"; | ||
| test(mpfr::RoundingMode::TowardZero); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| //===-- Unittests for atan2f ----------------------------------------------===// | ||
| // | ||
| // 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 "include/llvm-libc-macros/math-macros.h" | ||
| #include "src/__support/FPUtil/FPBits.h" | ||
| #include "src/errno/libc_errno.h" | ||
| #include "src/math/atan2f.h" | ||
| #include "test/UnitTest/FPMatcher.h" | ||
| #include "test/UnitTest/Test.h" | ||
|
|
||
| using LlvmLibcAtan2fTest = LIBC_NAMESPACE::testing::FPTest<float>; | ||
|
|
||
| TEST_F(LlvmLibcAtan2fTest, SpecialNumbers) { | ||
| LIBC_NAMESPACE::libc_errno = 0; | ||
|
|
||
| LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); | ||
| EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atan2f(aNaN, zero)); | ||
| EXPECT_FP_EXCEPTION(0); | ||
| EXPECT_MATH_ERRNO(0); | ||
|
|
||
| LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); | ||
| EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atan2f(1.0f, aNaN)); | ||
| EXPECT_FP_EXCEPTION(0); | ||
| EXPECT_MATH_ERRNO(0); | ||
|
|
||
| LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); | ||
| EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::atan2f(zero, zero)); | ||
| EXPECT_FP_EXCEPTION(0); | ||
| EXPECT_MATH_ERRNO(0); | ||
|
|
||
| LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); | ||
| EXPECT_FP_EQ_ALL_ROUNDING(-0.0f, LIBC_NAMESPACE::atan2f(-0.0f, zero)); | ||
| EXPECT_FP_EXCEPTION(0); | ||
| EXPECT_MATH_ERRNO(0); | ||
|
|
||
| LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); | ||
| EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::atan2f(1.0f, inf)); | ||
| EXPECT_FP_EXCEPTION(0); | ||
| EXPECT_MATH_ERRNO(0); | ||
|
|
||
| LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); | ||
| EXPECT_FP_EQ_ALL_ROUNDING(-0.0f, LIBC_NAMESPACE::atan2f(-1.0f, inf)); | ||
| EXPECT_FP_EXCEPTION(0); | ||
| EXPECT_MATH_ERRNO(0); | ||
| } |