Skip to content

[flang] Moved REAL(16) RANDOM_NUMBER to Float128Math library. #85002

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,24 @@

using namespace Fortran::runtime;

namespace {
/// Placeholder for real*16 version of RandomNumber Intrinsic
struct ForcedRandomNumberReal16 {
static constexpr const char *name = ExpandAndQuoteKey(RTNAME(RandomNumber16));
static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
return [](mlir::MLIRContext *ctx) {
auto boxTy =
fir::runtime::getModel<const Fortran::runtime::Descriptor &>()(ctx);
auto strTy = fir::runtime::getModel<const char *>()(ctx);
auto intTy = fir::runtime::getModel<int>()(ctx);
;
return mlir::FunctionType::get(ctx, {boxTy, strTy, intTy},
mlir::NoneType::get(ctx));
};
}
};
} // namespace

mlir::Value fir::runtime::genAssociated(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value pointer,
mlir::Value target) {
Expand Down Expand Up @@ -100,8 +118,15 @@ void fir::runtime::genRandomInit(fir::FirOpBuilder &builder, mlir::Location loc,

void fir::runtime::genRandomNumber(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value harvest) {
mlir::func::FuncOp func =
fir::runtime::getRuntimeFunc<mkRTKey(RandomNumber)>(loc, builder);
mlir::func::FuncOp func;
auto boxEleTy = fir::dyn_cast_ptrOrBoxEleTy(harvest.getType());
auto eleTy = fir::unwrapSequenceType(boxEleTy);
if (eleTy.isF128()) {
func = fir::runtime::getRuntimeFunc<ForcedRandomNumberReal16>(loc, builder);
} else {
func = fir::runtime::getRuntimeFunc<mkRTKey(RandomNumber)>(loc, builder);
}

mlir::FunctionType funcTy = func.getFunctionType();
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
mlir::Value sourceLine =
Expand Down
1 change: 1 addition & 0 deletions flang/runtime/Float128Math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ set(sources
nearest.cpp
norm2.cpp
pow.cpp
random.cpp
round.cpp
rrspacing.cpp
scale.cpp
Expand Down
23 changes: 23 additions & 0 deletions flang/runtime/Float128Math/random.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- runtime/Float128Math/random.cpp -----------------------------------===//
//
// 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 "math-entries.h"
#include "numeric-template-specs.h"
#include "random-templates.h"

using namespace Fortran::runtime::random;
extern "C" {

#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
void RTDEF(RandomNumber16)(
const Descriptor &harvest, const char *source, int line) {
return Generate<CppTypeFor<TypeCategory::Real, 16>, 113>(harvest);
}
#endif

} // extern "C"
87 changes: 87 additions & 0 deletions flang/runtime/random-templates.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//===-- runtime/random-templates.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 FORTRAN_RUNTIME_RANDOM_TEMPLATES_H_
#define FORTRAN_RUNTIME_RANDOM_TEMPLATES_H_

#include "lock.h"
#include "numeric-templates.h"
#include "flang/Runtime/descriptor.h"
#include <algorithm>
#include <random>

namespace Fortran::runtime::random {

// Newer "Minimum standard", recommended by Park, Miller, and Stockmeyer in
// 1993. Same as C++17 std::minstd_rand, but explicitly instantiated for
// permanence.
using Generator =
std::linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;

using GeneratedWord = typename Generator::result_type;
static constexpr std::uint64_t range{
static_cast<std::uint64_t>(Generator::max() - Generator::min() + 1)};
static constexpr bool rangeIsPowerOfTwo{(range & (range - 1)) == 0};
static constexpr int rangeBits{
64 - common::LeadingZeroBitCount(range) - !rangeIsPowerOfTwo};

extern Lock lock;
extern Generator generator;
extern std::optional<GeneratedWord> nextValue;

// Call only with lock held
static GeneratedWord GetNextValue() {
GeneratedWord result;
if (nextValue.has_value()) {
result = *nextValue;
nextValue.reset();
} else {
result = generator();
}
return result;
}

template <typename REAL, int PREC>
inline void Generate(const Descriptor &harvest) {
static constexpr std::size_t minBits{
std::max<std::size_t>(PREC, 8 * sizeof(GeneratedWord))};
using Int = common::HostUnsignedIntType<minBits>;
static constexpr std::size_t words{
static_cast<std::size_t>(PREC + rangeBits - 1) / rangeBits};
std::size_t elements{harvest.Elements()};
SubscriptValue at[maxRank];
harvest.GetLowerBounds(at);
{
CriticalSection critical{lock};
for (std::size_t j{0}; j < elements; ++j) {
while (true) {
Int fraction{GetNextValue()};
if constexpr (words > 1) {
for (std::size_t k{1}; k < words; ++k) {
static constexpr auto rangeMask{
(GeneratedWord{1} << rangeBits) - 1};
GeneratedWord word{(GetNextValue() - generator.min()) & rangeMask};
fraction = (fraction << rangeBits) | word;
}
}
fraction >>= words * rangeBits - PREC;
REAL next{
LDEXPTy<REAL>::compute(static_cast<REAL>(fraction), -(PREC + 1))};
if (next >= 0.0 && next < 1.0) {
*harvest.Element<REAL>(at) = next;
break;
}
}
harvest.IncrementSubscripts(at);
}
}
}

} // namespace Fortran::runtime::random

#endif // FORTRAN_RUNTIME_RANDOM_TEMPLATES_H_
81 changes: 6 additions & 75 deletions flang/runtime/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,85 +11,24 @@

#include "flang/Runtime/random.h"
#include "lock.h"
#include "random-templates.h"
#include "terminator.h"
#include "flang/Common/float128.h"
#include "flang/Common/leading-zero-bit-count.h"
#include "flang/Common/uint128.h"
#include "flang/Runtime/cpp-type.h"
#include "flang/Runtime/descriptor.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <limits>
#include <memory>
#include <random>
#include <time.h>

namespace Fortran::runtime {
namespace Fortran::runtime::random {

// Newer "Minimum standard", recommended by Park, Miller, and Stockmeyer in
// 1993. Same as C++17 std::minstd_rand, but explicitly instantiated for
// permanence.
using Generator =
std::linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;

using GeneratedWord = typename Generator::result_type;
static constexpr std::uint64_t range{
static_cast<std::uint64_t>(Generator::max() - Generator::min() + 1)};
static constexpr bool rangeIsPowerOfTwo{(range & (range - 1)) == 0};
static constexpr int rangeBits{
64 - common::LeadingZeroBitCount(range) - !rangeIsPowerOfTwo};

static Lock lock;
static Generator generator;
static std::optional<GeneratedWord> nextValue;

// Call only with lock held
static GeneratedWord GetNextValue() {
GeneratedWord result;
if (nextValue.has_value()) {
result = *nextValue;
nextValue.reset();
} else {
result = generator();
}
return result;
}

template <typename REAL, int PREC>
inline void Generate(const Descriptor &harvest) {
static constexpr std::size_t minBits{
std::max<std::size_t>(PREC, 8 * sizeof(GeneratedWord))};
using Int = common::HostUnsignedIntType<minBits>;
static constexpr std::size_t words{
static_cast<std::size_t>(PREC + rangeBits - 1) / rangeBits};
std::size_t elements{harvest.Elements()};
SubscriptValue at[maxRank];
harvest.GetLowerBounds(at);
{
CriticalSection critical{lock};
for (std::size_t j{0}; j < elements; ++j) {
while (true) {
Int fraction{GetNextValue()};
if constexpr (words > 1) {
for (std::size_t k{1}; k < words; ++k) {
static constexpr auto rangeMask{
(GeneratedWord{1} << rangeBits) - 1};
GeneratedWord word{(GetNextValue() - generator.min()) & rangeMask};
fraction = (fraction << rangeBits) | word;
}
}
fraction >>= words * rangeBits - PREC;
REAL next{std::ldexp(static_cast<REAL>(fraction), -(PREC + 1))};
if (next >= 0.0 && next < 1.0) {
*harvest.Element<REAL>(at) = next;
break;
}
}
harvest.IncrementSubscripts(at);
}
}
}
Lock lock;
Generator generator;
std::optional<GeneratedWord> nextValue;

extern "C" {

Expand Down Expand Up @@ -130,14 +69,6 @@ void RTNAME(RandomNumber)(
#if LDBL_MANT_DIG == 64
Generate<CppTypeFor<TypeCategory::Real, 10>, 64>(harvest);
return;
#endif
}
break;
case 16:
if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) {
#if LDBL_MANT_DIG == 113
Generate<CppTypeFor<TypeCategory::Real, 16>, 113>(harvest);
return;
#endif
}
break;
Expand Down Expand Up @@ -263,4 +194,4 @@ void RTNAME(RandomSeed)(const Descriptor *size, const Descriptor *put,
}

} // extern "C"
} // namespace Fortran::runtime
} // namespace Fortran::runtime::random
16 changes: 16 additions & 0 deletions flang/test/Lower/Intrinsics/random_number_real16.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
! RUN: bbc -emit-fir %s -o - | FileCheck %s
! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s

! CHECK-LABEL: func @_QPtest_scalar
! CHECK: fir.call @_FortranARandomNumber16({{.*}}){{.*}}: (!fir.box<none>, !fir.ref<i8>, i32) -> none
subroutine test_scalar
real(16) :: r
call random_number(r)
end

! CHECK-LABEL: func @_QPtest_array
! CHECK: fir.call @_FortranARandomNumber16({{.*}}){{.*}}: (!fir.box<none>, !fir.ref<i8>, i32) -> none
subroutine test_array(r)
real(16) :: r(:)
call random_number(r)
end