4 minutes
Written: 2021-07-30 17:11 +0000
Updated: 2024-08-06 00:52 +0000
GSoC21 W8: LFortran Refactors, and Compile Time Evaluations
This post is part of the GSoC21: LFortran series.
Language standardese and implementations
Background
Serialized update for the 2021 Google Summer of Code under the fortran-lang organization, mentored by Ondrej Certik.
Logistics
- Discussed more refactors over MRs and Zulip
Overview
Intrinsic functions and more bug hunting. A lot of starts in different directions, but I will need to trim these down a bit. A major goal was working through the compile time evaluation of some intrinsic functions.
New Merge Requests
- Split ast_to_asr
- An MR started last week, completed and approved this week
- tiny: Runtime implementation skeleton
- What will eventually be compiled, hooks into
C
for now - tiny: Compile time implementation
- The population of
value
fortiny
function calls - Draft: Shift runtime intrinsic design
- Harmonizing the code-base, much of this is cleaning up my own earlier math implementations
- Draft: expr_value for Kind
- A WIP MR which will clean up the slightly strange
extract_kind
function - Draft: Implement where construct
- An MR along the lines of
if
, related but distinct from Gagandeep’s masked optimization WIP
Freshly Assigned Issues
- –show-asr For larger values
- A visual glitch in the prettied output
Additional Tasks
Some of my earlier clean up MRs are beginning to stagnate (CI stuff), will have to catch up on them.
Misc Logs
The splitting of files for the refactor was harsh work. Took a few hours. Mindless, but really needs precision. Thankfully vim
folds help a whole lot. This was further enhanced by Ondrej to make things even cleaner.
Intrinsic Design
I have discussed the design and implementation of these a few times before, but perhaps another write up will give direction to my thoughts. We have two major points of contact with the intrinsic functions:
- Compile time
- These are intrinsic functions like
tiny
orkind
or evensin
which can be evaluated immediately to populate theexpr_t* value
object - Runtime
- These are the actual implementations, currently the goal is to have these hook into
C
libraries
Tiny Concerns
I am not a Fortran language lawyer, but I found myself puzzling over the legalese of the F-2018 draft standard with respect to the C++
nearest neighbor, std::numerical_limits
.
Essentially, the usage of tiny
is meant to facilitate doing mathematics without worrying about the exact representability of the value; that is:
1program main
2 implicit none
3 integer, parameter :: dp=kind(0.d0)
4 real :: a=1.0000009
5 if (abs(a-1)<tiny(1._dp)) then
6 error stop "a-1 is effectively 0"
7 end if
8end program
Now by definition, tiny
has the following properties:
- Description. Smallest positive model number
- Class. Inquiry function
- Argument. X shall be a real scalar or array
- Result Characteristics. Scalar with the same type and kind type parameter as X.
- Result Value. The result has the value \(b^{e_{min}-1}\) where \(b\) and \(e_{min}\) are defined in 16.4 for the model representing numbers of the same type and kind type parameter as X.
- Example. TINY(X) has the value \(2^{-127}\) for real X whose model is as in 16.4
This is fairly straightforward, once the model set for real \(x\) is understood as (from the section mentioned):
\[ x = 0 || x=s×bᵉ×∑_{k=1}ᵖfₖ×b^{-k} \]
Where \(b\) and \(p\) are integers exceeding one; each \(fₖ\) is a nonnegative integer less than \(b\), with \(f₁\) nonzero; \(s\) is \(+1\) or \(-1\); and \(e\) is an integer that lies between the integer minimum and maxima. An extended model for real kinds relaxes the range of the exponent.
So far so good. The C
equivalence is fairly straightforward, that is the FLT_MIN
and DBL_MIN
macros defined in <float.h>
. This is infact what gfortran
generates as well.
For a while though I was thrown by the fact that C++
, within <limits>
also has std::numeric_limits<T>lowest()
(described here), which is smaller than the corresponding min()
calls and has no direct C
equivalent. It is infact, specifically mentioned to not be min
for floating-point types.
However, I recognized soon enough from the implementation that there is no real conflict, as it is simply -max()
, which says nothing about the representability.
Therefore, tiny
must be FLT_MIN
or DBL_MIN
. I also took a small detour into std::variant
before going with good old if-else
early returns instead.
1int tiny_kind = LFortran::ASRUtils::extract_kind_from_ttype_t(tiny_type);
2if (tiny_kind ==4){
3 float low_val = std::numeric_limits<float>::min();
4 value = ASR::down_cast<ASR::expr_t>(ASR::make_ConstantReal_t(al, x.base.base.loc,
5 low_val, // value
6 tiny_type));
7} else {
8 double low_val = std::numeric_limits<double>::min();
9 value = ASR::down_cast<ASR::expr_t>(ASR::make_ConstantReal_t(al, x.base.base.loc,
10 low_val, // value
11 tiny_type));
12 }
Conclusions
My thoughts have been turning towards scalability for a long time now. Good design before it is necessary is a premature optimization, but I think I would like to formulate a cleaner way of dealing with the intrinsic functions which can be reduced to values. The ISO_C_BINDING
has been in my thoughts for a while now. Without it, runtime compilation of the ASR remains intractable. I expect the next week to continue along the same lines, populating value for all the intrinsic functions called by minidftatom
. It would be best to also generate corresponding runtime
implementations.
Series info
GSoC21: LFortran series
- GSoC21 W1: LFortran Kickoff
- GSoC21 W2: LFortran Unraveling
- GSoC21 W3: Kind, Characters, and Standards
- GSoC21 W4: LFortran, Backends and Bugs
- GSoC21 W5: LFortran Design Details and minidftatom
- GSoC21 W6: LFortran ASR and Values
- GSoC21 W7: LFortran Workflow Basics
- GSoC21 W8: LFortran Refactors, and Compile Time Evaluations <-- You are here!
- GSoC21 W9: LFortran Bug Hunting Bonanza
- GSoC21 W10: LFortran Runtime Library Design
- GSoC21 LFortran and Computational Chemistry