How to add an intrinsic to flang¶
This page contains a guide of how to add a new function/intrinsic to FLANG library. It was based on the intrinsics TRAILZ
and LEADZ
, which are math runtime intrinsics. Therefore all the steps may need some changes based on which type of intrinsic is being added to FLANG library. The function/intrinsic will be added inside <directory_where_cloned>/flang/runtime/flang
. runtime
is the fortran runtime library.
Step-by-step guide¶
How to link a new intrinsic in flang?
Create the .c (
your_intrinsic.c
) file in the directory e.g.runtime/flang
. It is common sense to create one file for each data type. For instance, numbers can be expressed asintegers
,long int
,short int
and so on. So in this case it is better to create one file for each intrinsic. Each one of these intrinsics has a different input according to its data type.In the directory
tools/flang1/utils/symtab
there are 2 filessymini_ftn.n
andsymini.cpp
that need to be changed. Symini adds a reference of the new intrinsic in the AST (Abstract Syntaxe Tree) usingsymini.cpp
andsymini_ft.n
.In
tools/flang1/utils/symtab/symini_ftn.n
add the lines below where it is suitable. This file is needed byast.n
because it has the symbols of the intrinsics to build the ILM file. You can look into the filesymini_ftn.n
to understand the syntax of the file.
i. .AT elemental I .H1 YOUR_INTRINSIC – ii. .AT elemental i .H7 YOUR_INTRINSIC --
In file
tools/flang1/utils/symtab/symini.cpp
find arrayconst char *SyminiFE90::init_namesX[]
(wherex
is a number) add your intrinsic between brackets" your_intrinsic "
. Do not know which one of theX``s (``SyminiFE90::init_namesX[]
) is the most correct, but I believe any one of them will work.
tools/flang1/flang1exe/
. Basically there are 3 files to that you need to modify, they are:lowerexp.c
,stout.c
andsemfunc.c
. The changes will allow flang1 to build the ILM file. ILM is the intermediate file between Fortran and flang2./tools/flang1/flang1exe/lowerexp.c
has routines used bylower.c
for lowering to ILMs.lower.c
creates the back-end ILM structure. It lowers the AST and the ILM for something that the next layer can use. You must add the reference of your intrinsic in two functions:In
static int intrinsic_arg_dtype(int intr, int ast, int args, int nargs)
add the line bellow and changeYOUR_INTRINSIC
for the name of the function you have implemented in upper case.
case I_ YOUR_INTRINSIC :
In
static int lower_intrinsic(int ast)
, add the line below and changeYOUR_INTRINSIC
for the name of the intrinsic you have implemented in upper case. The functionintrin_name_bsik (char *name, int ast)
recognises the type of the data that follows the intrinsic and writes in the ILM.
case I_ YOUR_INTRINSIC : ilm = intrin_name_bsik(" YOUR_INTRINSIC ", ast); break;
In file
/tools/flang1/flang1exe/astout.c
in the functionstatic void print_as t(int ast)
:
#ifdef I_YOUR_INTRINSIC case I_ YOUR_INTRINSIC : if (XBIT(49, 0x1040000)) /* T3D/T3E or C90 Cray targets */ put_call(ast, 0, NULL, 0); break; } rtlRtn = RTE_ your_intrinsic ; goto make_func_name; #endif
/tools/flang1/flang1exe/semfunc.c
is Fortran front-end utility routines used by Semantic Analyzer to process functions, subroutines, predeclares, etc. In the functionint ref_pd(SST *stktop, ITEM *list)
add the line below. The goal is to write in the ILM the pre-defined function.
case PD_ your_intrinsic :
The directory
tools/shared
has functions that provide the front-end access to the runtime library structure. You need also to change the filesrtlRtns.h
andrtlRtns.c
.rtlRtns.h
has the Enumerator for some (eventually all) the RTL library routines.rtlRtns.c
has the entries that must be sorted on thebaseNm
field. NOTE: make sure they are in the same order in the list, otherwise it may generate an error./tools/shared/rtlRtns.h
intypedef enum {
add:
RTE_your_intrinsic
/tools/shared/rtlRtns.c
inFtnRteRtn ftnRtlRtns[] = {
add:
{" your_intrinsic ", "", FALSE, ""},
tools/flang2/flang2exe
is responsible for the front-end of the stage 2[1], thereforeflang2exe
files are the ones that change the file from ILM to ILI and at the end to LLVM IR. In this directory you may need to change some files as well. Have a look atmath.h
,iliutil.cpp
andexp_ftn.cpp
./tools/flang2/flang2exe/mth.h
this library parameterizes the names of the__mth_i/__fmth_i ...
functions. In case the intrinsic has different behaviour for different inputs, then you should create one#define
function for each behavior, for instance:
#define MTH_I_I YOUR_INTRINSIC I "__mth_i_i your_intrinsic i". //for data type of 8 or 16 bits #define MTH_I_I YOUR_INTRINSIC "__mth_i_i your_intrinsic ". //for data type equals to 32 bits #define MTH_I_K YOUR_INTRINSIC "__mth_i_k your_intrinsic " //for data type equals to 64 bits
/tools/flang2/flang2exe/iliutil.cpp
has the ILI utility module. They contain the reference of your intrinsics created atmth.h
.void prilitree(int i) {
add reference to the intrinsic. Thegoto intrinsic
writes on thegbl.dbgfi
. Add one case statement for eachmth_i
created onmth.h
case IL_I YOUR_INTRINSIC I: //mth_i_i your_intrinsic i n = 2; opval = " your_intrinsic "; goto intrinsic; case IL_I YOUR_INTRINSIC : //mth_i_i your_intrinsic case IL_K YOUR_INTRINSIC : //mth_i_k your_intrinsic n = 1; opval = " your_intrinsic "; goto intrinsic;
/tools/flang2/flang2exe/exp_ftn.cpp
has the Fortran-specific expander routines. Invoid exp_ac(ILM_OP opc, ILM *ilmp, int curilm)
add the following lines.exp_ac()
is called ineval_ilm()
inexpand.cpp
. It looks likeexpand.cpp
file works on top of ILM and will expand it.
case IM_K YOUR_INTRINSIC : op1 = ILI_OF(ILM_OPND(ilmp, 1)); //takes the op from ILM to build the ILI if (XBIT(124, 0x400)). //124 is a mask and 0x400: 64 bits of precision for integer*8 and logical*8 operations [2]. ilix = ad1ili(IL_K YOUR_INTRINSIC , op1); // add op to ILI ad1ili(ILI_OP opc, int opn1 ) in iliutil.cpp else { op1 = kimove(op1); // kimove(int ilix) in iliutil.cpp ilix = ad1ili(IL_I YOUR_INTRINSIC , op1); } ILM_RESULT(curilm) = ilix.
/tools/flang2/utils/ilitp
has 3 more directories, one for each microprocessor architecture where Flang may be used : aarch64, ppc64le and x86_64 . In each of these directories there are filesilitp.n
,ilitp_longdouble.n
. According to what you are doing you need to change both. But usually you should change onlyilitp.n
in each one of the directories (aarch64, ppc64le and x86_64). They are responsible for writing ILI optimised for each architecture. Theiliutil.cpp
reads the ILI file with the help ofilitp.n
.IL I YOUR_INTRINCIS I irlnk stc 8-/16- bit integer YOUR_INTRINSIC intrinsic. The value, 0 or 1, of the second operand indicates 8-bit or 16-bit, respectively. .AT arth null ir cse .IL I YOUR_INTRINSIC irlnk 32-bit integer YOUR_INTRINSIC intrinsic. .AT arth null ir cse .CG "?????" 'l' .IL K YOUR_INTRINSIC krlnk 64-bit integer YOUR_INTRINSIC intrinsic. .AT arth null kr cse .CG "??????t" 'q'
/tools/flang2/utils/ilmtp/
has also 3 directories one for each architecture aarch64, xb6_64 and ppc64le . In these you will findilmtp.n
. You need to modify this file as well by adding the following lines:.IL B YOUR_INTRINSIC intr lnk 8-bit integer YOUR_INSTRINSIC intrinsic .OP I YOUR_INSTRINSIC I r p1 iv0 .IL S YOUR_INSTRINSIC intr lnk 16-bit integer YOUR_INSTRINSIC intrinsic .OP I YOUR_INSTRINSIC I r p1 iv1 .IL I YOUR_INSTRINSIC intr lnk 32-bit integer YOUR_INTRINSIC intrinsic .OP I YOUR_INSTRINSIC r p1 .IL K YOUR_INSTRINSIC intr lnk 64-bit integer YOUR_INSTRINSIC intrinsic .AT i8 .OP K YOUR_INSTRINSIC r p1
After all the modifications made above, it is needed to check if it works. First run make files in build-flang. Type the commands :
make -j48
make install
If you pass the make then it is time to build the test for the new intrinsic/function. I suggest before adding the test on flang, to create a local directory and write a small test for your new intrinsic. The directory
flang/test/f90_correct
has the tests for intrinsics. They run withmake check-all
. Insidef90_correct
there are 3 more directories, they are:inc
,lit
andsrc
.src
has fortran tests in.f90
, ininc
is the bash script to call the test function and run it, andinc
has sh files.After you have created your tests build flang again and type:
make -j48
make install
make check-all
, this last command will run the tests onf90_correct
and if something went wrong it will tell.
When the new intrinsic passed all the tests you can commit for review with the following commands:
git add .
git commit -m "message of the commit"
orgit commit --amend
; the first when it is the first time you commit and the second when you are just updating.git pull --rebase
;git review
You should check if the changes done in rebase are not affected. If so change what needs to be changed and apply step 10 again.