Mercurial > hg > CbC > CbC_llvm
view flang/runtime/random.cpp @ 207:2e18cbf3894f
LLVM12
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 08 Jun 2021 06:07:14 +0900 |
parents | |
children | 5f17cb93ff66 |
line wrap: on
line source
//===-- runtime/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 // //===----------------------------------------------------------------------===// // Implements the intrinsic subroutines RANDOM_INIT, RANDOM_NUMBER, and // RANDOM_SEED. #include "random.h" #include "cpp-type.h" #include "descriptor.h" #include "lock.h" #include "flang/Common/leading-zero-bit-count.h" #include "flang/Common/uint128.h" #include <algorithm> #include <cmath> #include <cstdint> #include <ctime> #include <limits> #include <memory> #include <random> namespace Fortran::runtime { // 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; 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) { Int fraction{generator()}; if constexpr (words > 1) { for (std::size_t k{1}; k < words; ++k) { static constexpr auto rangeMask{(GeneratedWord{1} << rangeBits) - 1}; GeneratedWord word{(generator() - generator.min()) & rangeMask}; fraction = (fraction << rangeBits) | word; } } fraction >>= words * rangeBits - PREC; *harvest.Element<REAL>(at) = std::ldexp(static_cast<REAL>(fraction), -(PREC + 1)); harvest.IncrementSubscripts(at); } } } extern "C" { void RTNAME(RandomInit)(bool repeatable, bool /*image_distinct*/) { // TODO: multiple images and image_distinct: add image number { CriticalSection critical{lock}; if (repeatable) { generator.seed(0); } else { generator.seed(std::time(nullptr)); } } } void RTNAME(RandomNumber)( const Descriptor &harvest, const char *source, int line) { Terminator terminator{source, line}; auto typeCode{harvest.type().GetCategoryAndKind()}; RUNTIME_CHECK(terminator, typeCode && typeCode->first == TypeCategory::Real); int kind{typeCode->second}; switch (kind) { // TODO: REAL (2 & 3) case 4: Generate<CppTypeFor<TypeCategory::Real, 4>, 24>(harvest); break; case 8: Generate<CppTypeFor<TypeCategory::Real, 8>, 53>(harvest); break; #if LONG_DOUBLE == 80 case 10: Generate<CppTypeFor<TypeCategory::Real, 10>, 64>(harvest); break; #elif LONG_DOUBLE == 128 case 4: Generate<CppTypeFor<TypeCategory::Real, 16>, 113>(harvest); break; #endif default: terminator.Crash("RANDOM_NUMBER(): unsupported REAL kind %d", kind); } } void RTNAME(RandomSeedSize)( const Descriptor &size, const char *source, int line) { Terminator terminator{source, line}; auto typeCode{size.type().GetCategoryAndKind()}; RUNTIME_CHECK(terminator, size.rank() == 0 && typeCode && typeCode->first == TypeCategory::Integer); int kind{typeCode->second}; switch (kind) { case 4: *size.OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = 1; break; case 8: *size.OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = 1; break; default: terminator.Crash("RANDOM_SEED(SIZE=): bad kind %d\n", kind); } } void RTNAME(RandomSeedPut)( const Descriptor &put, const char *source, int line) { Terminator terminator{source, line}; auto typeCode{put.type().GetCategoryAndKind()}; RUNTIME_CHECK(terminator, put.rank() == 1 && typeCode && typeCode->first == TypeCategory::Integer && put.GetDimension(0).Extent() >= 1); int kind{typeCode->second}; GeneratedWord seed; switch (kind) { case 4: seed = *put.OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>(); break; case 8: seed = *put.OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>(); break; default: terminator.Crash("RANDOM_SEED(PUT=): bad kind %d\n", kind); } { CriticalSection critical{lock}; generator.seed(seed); } } void RTNAME(RandomSeedDefaultPut)() { // TODO: should this be time &/or image dependent? { CriticalSection critical{lock}; generator.seed(0); } } void RTNAME(RandomSeedGet)( const Descriptor &got, const char *source, int line) { Terminator terminator{source, line}; auto typeCode{got.type().GetCategoryAndKind()}; RUNTIME_CHECK(terminator, got.rank() == 1 && typeCode && typeCode->first == TypeCategory::Integer && got.GetDimension(0).Extent() >= 1); int kind{typeCode->second}; GeneratedWord seed; { CriticalSection critical{lock}; seed = generator(); generator.seed(seed); } switch (kind) { case 4: *got.OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = seed; break; case 8: *got.OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = seed; break; default: terminator.Crash("RANDOM_SEED(GET=): bad kind %d\n", kind); } } } // extern "C" } // namespace Fortran::runtime