Language standardese and implementations

## Background

Serialized update for the 2021 Google Summer of Code under the fortran-lang organization, mentored by Ondrej Certik.

### Series

This post is part of a series based around my weekly GSoC21 project check-ins.

1. GSoC21 W1: LFortran Kickoff
2. GSoC21 W2: LFortran Unraveling
3. GSoC21 W3: Kind, Characters, and Standards
4. GSoC21 W4: LFortran, Backends and Bugs
5. GSoC21 W5: LFortran Design Details and minidftatom
6. GSoC21 W6: LFortran ASR and Values
7. GSoC21 W7: LFortran Workflow Basics
8. GSoC21 W8: LFortran Refactors, and Compile Time Evaluations <– You are here!

## 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 for tiny 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

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 or kind or even sin which can be evaluated immediately to populate the expr_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:

1. Description. Smallest positive model number
2. Class. Inquiry function
3. Argument. X shall be a real scalar or array
4. Result Characteristics. Scalar with the same type and kind type parameter as X.
5. 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.
6. 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.