From 2870109f53ee196fcd0e6e2f08c0a276024d58c0 Mon Sep 17 00:00:00 2001 From: Peixin Qiao Date: Thu, 14 Jul 2022 10:12:59 +0800 Subject: [PATCH] [flang] Add patch for inline runtime functions Add patch for inline runtime functions. This is done in flang2. --- 1-flang-runtime-inline.patch | 1226 ++++++++++++++++++++++++++++++++++ flang.spec | 5 +- 2 files changed, 1230 insertions(+), 1 deletion(-) create mode 100644 1-flang-runtime-inline.patch diff --git a/1-flang-runtime-inline.patch b/1-flang-runtime-inline.patch new file mode 100644 index 0000000..b3f03c7 --- /dev/null +++ b/1-flang-runtime-inline.patch @@ -0,0 +1,1226 @@ +commit fe297c405e2b0cab6dd02c41d8f5889d896a2406 +Author: q00576763 +Date: Fri Jul 8 16:14:21 2022 +0800 + + [Flang2] Create one framework to inline f90-prefix runtime call + + This patch creates one framwork to inline f90-prefix runtime call before + writing out the generated IR. This also implements the inline of + f90_strcmp_klen, which is for the comparison of two strings. + +diff --git a/test/f90_correct/inc/inline_f90_strcmp_klen.mk b/test/f90_correct/inc/inline_f90_strcmp_klen.mk +new file mode 100644 +index 00000000..a2f1f193 +--- /dev/null ++++ b/test/f90_correct/inc/inline_f90_strcmp_klen.mk +@@ -0,0 +1,22 @@ ++# ++# 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 ++# ++ ++$(TEST): run ++ ++ ++build: $(SRC)/$(TEST).f90 ++ -$(RM) $(TEST).$(EXESUFFIX) core *.d *.mod FOR*.DAT FTN* ftn* fort.* ++ @echo ------------------------------------ building test $@ ++ -$(FC) -c $(FFLAGS) $(LDFLAGS) $(SRC)/$(TEST).f90 -Mx,218,0x1 -o $(TEST).$(OBJX) ++ -$(FC) $(FFLAGS) $(LDFLAGS) $(TEST).$(OBJX) $(LIBS) -o $(TEST).$(EXESUFFIX) ++ ++ ++run: ++ @echo ------------------------------------ executing test $(TEST) ++ $(TEST).$(EXESUFFIX) ++ ++verify: ; ++ +diff --git a/test/f90_correct/inc/inline_f90_strcmp_klen_2.mk b/test/f90_correct/inc/inline_f90_strcmp_klen_2.mk +new file mode 100644 +index 00000000..a2f1f193 +--- /dev/null ++++ b/test/f90_correct/inc/inline_f90_strcmp_klen_2.mk +@@ -0,0 +1,22 @@ ++# ++# 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 ++# ++ ++$(TEST): run ++ ++ ++build: $(SRC)/$(TEST).f90 ++ -$(RM) $(TEST).$(EXESUFFIX) core *.d *.mod FOR*.DAT FTN* ftn* fort.* ++ @echo ------------------------------------ building test $@ ++ -$(FC) -c $(FFLAGS) $(LDFLAGS) $(SRC)/$(TEST).f90 -Mx,218,0x1 -o $(TEST).$(OBJX) ++ -$(FC) $(FFLAGS) $(LDFLAGS) $(TEST).$(OBJX) $(LIBS) -o $(TEST).$(EXESUFFIX) ++ ++ ++run: ++ @echo ------------------------------------ executing test $(TEST) ++ $(TEST).$(EXESUFFIX) ++ ++verify: ; ++ +diff --git a/test/f90_correct/lit/inline_f90_strcmp_klen.sh b/test/f90_correct/lit/inline_f90_strcmp_klen.sh +new file mode 100644 +index 00000000..3880a96e +--- /dev/null ++++ b/test/f90_correct/lit/inline_f90_strcmp_klen.sh +@@ -0,0 +1,9 @@ ++# ++# 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 ++ ++# Shared lit script for each tests. Run bash commands that run tests with make. ++ ++# RUN: KEEP_FILES=%keep FLAGS=%flags TEST_SRC=%s MAKE_FILE_DIR=%S/.. bash %S/runmake | tee %t ++# RUN: cat %t | FileCheck %S/runmake +diff --git a/test/f90_correct/lit/inline_f90_strcmp_klen_2.sh b/test/f90_correct/lit/inline_f90_strcmp_klen_2.sh +new file mode 100644 +index 00000000..3880a96e +--- /dev/null ++++ b/test/f90_correct/lit/inline_f90_strcmp_klen_2.sh +@@ -0,0 +1,9 @@ ++# ++# 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 ++ ++# Shared lit script for each tests. Run bash commands that run tests with make. ++ ++# RUN: KEEP_FILES=%keep FLAGS=%flags TEST_SRC=%s MAKE_FILE_DIR=%S/.. bash %S/runmake | tee %t ++# RUN: cat %t | FileCheck %S/runmake +diff --git a/test/f90_correct/src/inline_f90_strcmp_klen.f90 b/test/f90_correct/src/inline_f90_strcmp_klen.f90 +new file mode 100644 +index 00000000..c407b901 +--- /dev/null ++++ b/test/f90_correct/src/inline_f90_strcmp_klen.f90 +@@ -0,0 +1,93 @@ ++! 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 ++ ++! Program to test the inline of f90_strcmp_klen ++ ++program main ++ character(4) :: s4_1, s4_2 ++ character(3) :: s3 ++ character(5) :: s5 ++ ++ s4_1 = "aaab" ++ s4_2 = "aaaa" ++ if (s4_1 <= s4_2 .or. str_le(s4_1, s4_2)) stop 1 ++ ++ s4_1 = "aaab" ++ s4_2 = "aaab" ++ if (s4_1 /= s4_2 .or. str_ne(s4_1, s4_2)) stop 2 ++ ++ s4_1 = "aaab" ++ s4_2 = "aaac" ++ if (s4_1 >= s4_2 .or. str_ge(s4_1, s4_2)) stop 3 ++ ++ s4_1 = "aaba" ++ s3 = "aaa" ++ if (s4_1 <= s3 .or. str_le(s4_1, s3)) stop 4 ++ ++ s4_1 = "aaba" ++ s3 = "aab" ++ if (s4_1 <= s3 .or. str_le(s4_1, s3)) stop 5 ++ ++ s4_1 = "aaba" ++ s3 = "aac" ++ if (s4_1 >= s3 .or. str_ge(s4_1, s3)) stop 6 ++ ++ s4_1 = "aab" ++ s3 = "aab" ++ if (s4_1 >= s3 .or. str_ge(s4_1, s3)) stop 7 ++ ++ s4_1 = "aab " ++ s3 = "aab" ++ if (s4_1 /= s3 .or. str_ne(s4_1, s3)) stop 8 ++ ++ s4_1 = "aaba" ++ s3 = "aab" ++ if (s4_1 <= s3 .or. str_le(s4_1, s3)) stop 9 ++ ++ s3 = "aab" ++ s4_2 = "aaaa" ++ if (s3 <= s4_2 .or. str_le(s3, s4_2)) stop 10 ++ ++ s3 = "aab" ++ s4_2 = "aaba" ++ if (s3 >= s4_2 .or. str_ge(s3, s4_2)) stop 11 ++ ++ s3 = "aab" ++ s4_2 = "aaca" ++ if (s3 >= s4_2 .or. str_ge(s3, s4_2)) stop 12 ++ ++ s3 = "aab" ++ s4_2 = "aab" ++ if (s3 <= s4_2 .or. str_le(s3, s4_2)) stop 13 ++ ++ s3 = "aab" ++ s4_2 = "aab " ++ if (s3 /= s4_2 .or. str_ne(s3, s4_2)) stop 14 ++ ++ s3 = "aab" ++ s4_2 = "aaba" ++ if (s3 >= s4_2 .or. str_ge(s3, s4_2)) stop 15 ++ ++ print *, "PASS" ++ ++contains ++ function str_le(s, t) result(res) ++ character(*) :: s, t ++ logical :: res ++ res = .false. ++ if (s <= t) res = .true. ++ end ++ function str_ge(s, t) result(res) ++ character(*) :: s, t ++ logical :: res ++ res = .false. ++ if (s >= t) res = .true. ++ end ++ function str_ne(s, t) result(res) ++ character(*) :: s, t ++ logical :: res ++ res = .false. ++ if (s /= t) res = .true. ++ end ++end +diff --git a/test/f90_correct/src/inline_f90_strcmp_klen_2.f90 b/test/f90_correct/src/inline_f90_strcmp_klen_2.f90 +new file mode 100644 +index 00000000..49bec361 +--- /dev/null ++++ b/test/f90_correct/src/inline_f90_strcmp_klen_2.f90 +@@ -0,0 +1,23 @@ ++! 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 ++ ++! Program to test the inline of f90_strcmp_klen when there is no constants ++! in procedure where the string compare exists. ++ ++program main ++ character(len=10):: a, b ++ a = '1111111112' ++ b = '1111 11111' ++ ++ if(func(a, b)) stop 1 ++ ++ print *, "PASS" ++contains ++ function func(a, b) ++ character(len =10) :: a, b ++ logical :: func ++ func = (a <= b) ++ end function ++end ++ +diff --git a/test/llvm_ir_correct/inline_strcmp_klen.f90 b/test/llvm_ir_correct/inline_strcmp_klen.f90 +new file mode 100644 +index 00000000..ca959ca5 +--- /dev/null ++++ b/test/llvm_ir_correct/inline_strcmp_klen.f90 +@@ -0,0 +1,19 @@ ++! ++! 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 ++! ++ ++! RUN: %flang -S -Mx,218,0x1 -O3 -emit-llvm %s -o - 2>&1 | FileCheck %s -check-prefix=INLINE ++! RUN: %flang -S -O3 -emit-llvm %s -o - 2>&1 | FileCheck %s -check-prefix=NOINLINE ++ ++program t ++ character(2) :: a, b ++ a = "aa" ++ b = "aa" ++ ++ ! INLINE-NOT: f90_strcmp_klen ++ ! NOINLINE: f90_strcmp_klen ++ if (a /= b) stop 1 ++ ++end program +diff --git a/tools/flang2/docs/xflag.n b/tools/flang2/docs/xflag.n +index 4108e594..a821af89 100644 +--- a/tools/flang2/docs/xflag.n ++++ b/tools/flang2/docs/xflag.n +@@ -5508,7 +5508,8 @@ POWER Modifications + Enable auto initialization of stack memory to 64bit signaling NaNs. + + .XF "218:" +-reserved ++.XB 0x01: ++Try to inline Fortran runtime calls (functions with f90 prefix). + + .XF "220:" + Enable tuning code for -Minline. +diff --git a/tools/flang2/flang2exe/CMakeLists.txt b/tools/flang2/flang2exe/CMakeLists.txt +index b7c749b1..713c7375 100644 +--- a/tools/flang2/flang2exe/CMakeLists.txt ++++ b/tools/flang2/flang2exe/CMakeLists.txt +@@ -77,6 +77,7 @@ set(SOURCES + verify.cpp + ompaccel.cpp + tgtutil.cpp ++ inline_runtime.cpp + ${UTILS_SYMTAB_BIN_DIR}/symtabdf.cpp + ) + +diff --git a/tools/flang2/flang2exe/cgmain.cpp b/tools/flang2/flang2exe/cgmain.cpp +index 3c08aa26..ac89bf48 100644 +--- a/tools/flang2/flang2exe/cgmain.cpp ++++ b/tools/flang2/flang2exe/cgmain.cpp +@@ -41,6 +41,7 @@ + #include "main.h" + #include "symfun.h" + #include "ilidir.h" ++#include "inline_runtime.h" + + #ifdef OMP_OFFLOAD_LLVM + #include "ompaccel.h" +@@ -389,6 +390,7 @@ static OPERAND *convert_float_size(OPERAND *, LL_Type *); + static int follow_sptr_hashlk(SPTR sptr); + static DTYPE follow_ptr_dtype(DTYPE); + static bool same_op(OPERAND *, OPERAND *); ++static void inline_runtime_calls(); + static void write_instructions(LL_Module *); + static LLIntegerConditionCodes convert_to_llvm_intcc(CC_RELATION cc); + static LLIntegerConditionCodes convert_to_llvm_uintcc(CC_RELATION cc); +@@ -1893,6 +1895,10 @@ restartConcur: + sched_instructions(Instructions); + } + ++ /* Inline f90 runtime calls */ ++ if (XBIT(218, 0x1)) ++ inline_runtime_calls(); ++ + /* print out the instructions */ + write_instructions(current_module); + +@@ -2828,6 +2834,21 @@ should_suppress_debug_loc(INSTR_LIST *instrs) + } + } + ++static void ++inline_runtime_calls() ++{ ++ INSTR_LIST *instr; ++ ++ for (instr = Instructions; instr; instr = instr->next) { ++ if (instr->i_name == I_CALL) { ++ INLINE_RUNTIME_ID inline_runtime_id = get_inline_runtime_id(instr); ++ if (inline_runtime_id != NONE_ID) ++ instr = ++ emit_inline_runtime_call(Instructions, instr, inline_runtime_id); ++ } ++ } ++} ++ + /** + \brief Write the instruction list to the LLVM IR output file + */ +diff --git a/tools/flang2/flang2exe/inline_runtime.cpp b/tools/flang2/flang2exe/inline_runtime.cpp +new file mode 100644 +index 00000000..dc09aa67 +--- /dev/null ++++ b/tools/flang2/flang2exe/inline_runtime.cpp +@@ -0,0 +1,815 @@ ++/* ++ * 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 ++ * ++ */ ++ ++/** ++ \file ++ \brief Inline f90 runtime call operations ++ */ ++ ++#include "inline_runtime.h" ++#include ++#include ++#include ++ ++enum OT_CC_NAMES { ++ NONE = 0, ++ OT_EQ, ++ OT_NE, ++ OT_UGT, ++ OT_UGE, ++ OT_ULT, ++ OT_ULE, ++ OT_SGT, ++ OT_SGE, ++ OT_SLT, ++ OT_SLE, ++}; ++ ++static std::unordered_map inline_calls; ++static LL_MDRef dbg_line_op; ++static unsigned int wrap_nmptr = 0; ++static unsigned long long wrap_max_cnt = 0; ++static INSTR_LIST *curr_instr = nullptr; ++static INSTR_LIST *top_instr = nullptr; ++static INSTR_LIST *runtime_instr = nullptr; ++static bool is_initialized_inline_calls = false; ++ ++static SPTR mklabel(const char *nmptr); ++static OPERAND *make_ot_cc_op(OT_CC_NAMES ot_cc); ++static OPERAND *mirror_nontmp_op(OPERAND *oper); ++static void mirror_instr(INSTR_LIST *, INSTR_LIST *); ++static INSTR_LIST *gen_instr(LL_InstrName, LL_Type *, OPERAND *); ++static INSTR_LIST *gen_instr(LL_InstrName, TMPS *, LL_Type *, OPERAND *); ++static INSTR_LIST *gen_new_st(INSTR_LIST *, OPERAND *, OPERAND *); ++static INSTR_LIST *gen_alloca_instr(LL_Type *); ++static INSTR_LIST *gen_new_instr(INSTR_LIST *, LL_InstrName, LL_Type *, ++ OPERAND *, OPERAND *, OPERAND *); ++static void link_instrs(INSTR_LIST *, INSTR_LIST *); ++static void inc_loop_iter(OPERAND *, LL_Type *, OPERAND *); ++static void cmp_two_vars(OPERAND *, OPERAND *, LL_Type *, OT_CC_NAMES); ++static void finalize_bb(SPTR, OPERAND *, LL_Type *); ++static void store_result_bb(SPTR, OPERAND *, OPERAND *, OPERAND *); ++static void process_strlen_lt_zero(OPERAND *); ++static INSTR_LIST *loop_s1_cmp_s2(OPERAND *, OPERAND *, OPERAND *, OPERAND *, ++ int); ++static INSTR_LIST *inline_f90_strcmp_klen(); ++static void init_inline_runtime_call(); ++ ++static SPTR ++mklabel(const char *nm) ++{ ++ SPTR sptr; ++ std::string s = nm; ++ ++ /* The same name may be called multiple times, so wrap the name ptr. */ ++ if (wrap_nmptr == UINT_MAX) { ++ wrap_max_cnt++; ++ wrap_nmptr = 0; ++ } ++ s += std::string("wrap"); ++ for (unsigned long long i = 0; i < wrap_max_cnt; i++) { ++ s += std::to_string(UINT_MAX); ++ s += std::string("wrap"); ++ } ++ s += std::to_string(wrap_nmptr++); ++ const char *nmptr = s.c_str(); ++ ++ sptr = getsym(nmptr, strlen(nmptr)); ++ STYPEP(sptr, ST_LABEL); ++ CCSYMP(sptr, 1); ++ return sptr; ++} ++ ++static OPERAND * ++make_ot_cc_op(OT_CC_NAMES ot_cc) ++{ ++ OPERAND *op; ++ ++ op = make_operand(); ++ op->ot_type = OT_CC; ++ op->ll_type = make_int_lltype(32); ++ op->val.cc = ot_cc; ++ return op; ++} ++ ++static OPERAND * ++mirror_nontmp_op(OPERAND *oper) ++{ ++ OPERAND *op; ++ ++ op = make_operand(); ++ op->ot_type = oper->ot_type; ++ op->ll_type = oper->ll_type; ++ op->val.sptr = oper->val.sptr; ++ return op; ++} ++ ++static void ++mirror_instr(INSTR_LIST *dest_instr, INSTR_LIST *src_instr) ++{ ++ dest_instr->rank = src_instr->rank; ++ dest_instr->i_name = src_instr->i_name; ++ dest_instr->flags = src_instr->flags; ++ dest_instr->ll_type = src_instr->ll_type; ++ dest_instr->operands = src_instr->operands; ++ dest_instr->dbg_line_op = src_instr->dbg_line_op; ++ dest_instr->tmps->info.idef = dest_instr; ++} ++ ++static INSTR_LIST * ++gen_instr(LL_InstrName instr_name, LL_Type *ll_type, OPERAND *operands) ++{ ++ return gen_instr(instr_name, make_tmps(), ll_type, operands); ++} ++ ++static INSTR_LIST * ++gen_instr(LL_InstrName instr_name, TMPS *tmps, LL_Type *ll_type, ++ OPERAND *operands) ++{ ++ INSTR_LIST *iptr; ++ ++ iptr = (INSTR_LIST *)getitem(LLVM_LONGTERM_AREA, sizeof(INSTR_LIST)); ++ memset(iptr, 0, sizeof(INSTR_LIST)); ++ iptr->i_name = instr_name; ++ switch (instr_name) { ++ default: ++ iptr->dbg_line_op = dbg_line_op; ++ break; ++ case I_NONE: ++ case I_DECL: ++ case I_CLEANUP: ++ case I_CATCH: ++ case I_ALLOCA: ++ break; ++ } ++ iptr->tmps = tmps; ++ if (tmps != NULL) ++ iptr->tmps->info.idef = iptr; ++ iptr->ll_type = ll_type; ++ iptr->operands = operands; ++ return iptr; ++} ++ ++static INSTR_LIST * ++gen_alloca_instr(LL_Type *ll_type) ++{ ++ INSTR_LIST *new_instr; ++ OPERAND *opnd = make_tmp_op(ll_type, make_tmps()); ++ new_instr = gen_instr(I_ALLOCA, opnd->tmps, ll_type, NULL); ++ return new_instr; ++} ++ ++static INSTR_LIST * ++gen_new_st(INSTR_LIST *instr, OPERAND *opnd0, OPERAND *opnd1) ++{ ++ INSTR_LIST *new_instr; ++ ++ opnd0->next = opnd1; ++ new_instr = gen_instr(I_STORE, NULL, NULL, opnd0); ++ instr->next = new_instr; ++ new_instr->prev = instr; ++ return new_instr; ++} ++ ++static INSTR_LIST * ++gen_new_instr(INSTR_LIST *instr, LL_InstrName i_name, LL_Type *ll_type, ++ OPERAND *opnd0, OPERAND *opnd1, OPERAND *opnd2) ++{ ++ INSTR_LIST *new_instr; ++ ++ if (opnd1 != NULL) ++ opnd0->next = opnd1; ++ if (opnd2 != NULL) ++ opnd1->next = opnd2; ++ new_instr = gen_instr(i_name, ll_type, opnd0); ++ instr->next = new_instr; ++ new_instr->prev = instr; ++ return new_instr; ++} ++ ++static void ++link_instrs(INSTR_LIST *instr1, INSTR_LIST *instr2) ++{ ++ instr1->next = instr2; ++ instr2->prev = instr1; ++} ++ ++/** ++ \brief Increment the loop iterator by inc_opnd. ++ */ ++static void ++inc_loop_iter(OPERAND *iter_opnd, LL_Type *ll_type, OPERAND *inc_opnd) ++{ ++ OPERAND *curr0_opnd; ++ ++ // %1 = load ll_type, ll_type* iter ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, ll_type, iter_opnd, NULL, NULL); ++ ++ // %2 = add nsw ll_type %1, inc_opnd ++ curr0_opnd = make_tmp_op(ll_type, curr_instr->tmps); ++ curr_instr = ++ gen_new_instr(curr_instr, I_ADD, ll_type, curr0_opnd, inc_opnd, NULL); ++ curr_instr->flags = NOSIGNEDWRAP; ++ ++ // store i64 %2, i64* %iter ++ curr0_opnd = make_tmp_op(ll_type, curr_instr->tmps); ++ curr_instr = gen_new_st(curr_instr, curr0_opnd, iter_opnd); ++} ++ ++/** ++ \brief Compare two variables, which needs to be loaded. ++ */ ++static void ++cmp_two_vars(OPERAND *var1, OPERAND *var2, LL_Type *ll_type, OT_CC_NAMES ot_cc) ++{ ++ INSTR_LIST *cmp1, *cmp2; ++ OPERAND *curr0_opnd, *curr1_opnd, *curr2_opnd; ++ LL_Type *i1_ty = make_int_lltype(1); ++ ++ // %1 = load ll_type, ll_type* var1 ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, ll_type, var1, NULL, NULL); ++ cmp1 = curr_instr; ++ ++ // %2 = load ll_type, ll_type* %var2 ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, ll_type, var2, NULL, NULL); ++ cmp2 = curr_instr; ++ ++ // %3 = icmp ot_cc ll_type %1, %2 ++ curr0_opnd = make_ot_cc_op(ot_cc); ++ curr1_opnd = make_tmp_op(ll_type, cmp1->tmps); ++ curr2_opnd = make_tmp_op(ll_type, cmp2->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++} ++ ++/** ++ \brief Make the final_bb label and load the return value. ++ */ ++static void ++finalize_bb(SPTR final_bb_label, OPERAND *result, LL_Type *ll_type) ++{ ++ OPERAND *curr0_opnd; ++ ++ curr0_opnd = make_label_op(final_bb_label); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // load ll_type, ll_type* result ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, ll_type, result, NULL, NULL); ++ ++ if (runtime_instr->ll_type != ll_type) { ++ // trunc ll_type result to (target type) ++ curr0_opnd = make_tmp_op(ll_type, curr_instr->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_TRUNC, runtime_instr->ll_type, ++ curr0_opnd, NULL, NULL); ++ } ++} ++ ++/** ++ \brief In \res_label bb, store \value into \result and branch to final bb. ++ */ ++static void ++store_result_bb(SPTR res_label, OPERAND *value, OPERAND *result, ++ OPERAND *final_bb) ++{ ++ OPERAND *curr0_opnd; ++ ++ curr0_opnd = make_label_op(res_label); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // store ll_type value, ll_type* result ++ curr_instr = gen_new_st(curr_instr, value, result); ++ ++ // br label %final_bb ++ curr_instr = gen_new_instr(curr_instr, I_BR, NULL, final_bb, NULL, NULL); ++} ++ ++/** ++ \brief Assign 0 to the string length if it is less than 0. ++ */ ++static void ++process_strlen_lt_zero(OPERAND *strlen_opnd) ++{ ++ OPERAND *curr0_opnd, *curr1_opnd, *curr2_opnd; ++ LL_Type *i1_ty = make_int_lltype(1); ++ LL_Type *i64_ty = make_int_lltype(64); ++ ++ // %1 = load i64, i64* %strlen ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, i64_ty, strlen_opnd, NULL, NULL); ++ ++ // %2 = icmp sge i64 %1, 0 ++ curr0_opnd = make_ot_cc_op(OT_SGE); ++ curr1_opnd = make_tmp_op(i64_ty, curr_instr->tmps); ++ curr2_opnd = make_constval_op(i64_ty, 0, 0); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++ ++ // br i1 %2, label %ahead, label %strlen_zero ++ SPTR ahead = mklabel("ahead"); ++ SPTR strlen_zero = mklabel("strlen_zero"); ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(ahead); ++ curr2_opnd = make_target_op(strlen_zero); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // Label strlen_zero ++ curr0_opnd = make_label_op(strlen_zero); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // store i64 0, i64* strlen ++ curr0_opnd = make_constval_op(i64_ty, 0, 0); ++ curr_instr = gen_new_st(curr_instr, curr0_opnd, strlen_opnd); ++ ++ // Label ahead ++ curr0_opnd = make_label_op(ahead); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++} ++ ++/** ++ \brief Compare the string s1 and s2 character by character. ++ ++ iter_opnd: the loop iteration operand ++ len_opnd: max(length of string s1, length of string s2) ++ mode: 1 when s1 and s2 are not empty; ++ 2 when s2 is empty, and its element is taken as the space character; ++ 3 when s1 is empty, and its element is taken as the space character; ++ */ ++static INSTR_LIST * ++loop_s1_cmp_s2(OPERAND *iter_opnd, OPERAND *len_opnd, OPERAND *result_one_opnd, ++ OPERAND *result_minus_one_opnd, int mode) ++{ ++ INSTR_LIST *cmp1, *cmp2, *save_tmps_instr; ++ OPERAND *curr0_opnd, *curr1_opnd, *curr2_opnd, *opnd_zero, *final_opnd; ++ INSTR_LIST *s1_instr = runtime_instr->operands->next->tmps->info.idef; ++ INSTR_LIST *s2_instr = runtime_instr->operands->next->next->tmps->info.idef; ++ ++ LL_Type *i1_ty = make_int_lltype(1); ++ LL_Type *i8_ty = make_int_lltype(8); ++ LL_Type *i32_ty = make_int_lltype(32); ++ LL_Type *i64_ty = make_int_lltype(64); ++ LL_Type *ptr_i8_ty = make_ptr_lltype(i8_ty); ++ ++ // Label loop_header ++ SPTR loop_header = mklabel("loop_header"); ++ curr0_opnd = make_label_op(loop_header); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // %1 = load i64, i64* %iter ++ curr_instr = gen_new_instr(curr_instr, I_LOAD, i64_ty, iter_opnd, NULL, NULL); ++ cmp1 = curr_instr; ++ ++ // %2 = load i64, i64* %s_len ++ curr_instr = gen_new_instr(curr_instr, I_LOAD, i64_ty, len_opnd, NULL, NULL); ++ cmp2 = curr_instr; ++ ++ // %3 = icmp sle i64 %1, %2 ++ curr0_opnd = make_ot_cc_op(OT_SLE); ++ curr1_opnd = make_tmp_op(i64_ty, cmp1->tmps); ++ curr2_opnd = make_tmp_op(i64_ty, cmp2->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++ ++ // br i1 %3, label %iter_le_s_len, label %iter_gt_s_len ++ SPTR iter_le_s_len = mklabel("iter_le_s_len"); ++ SPTR iter_gt_s_len = mklabel("iter_gt_s_len"); ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(iter_le_s_len); ++ curr2_opnd = make_target_op(iter_gt_s_len); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // -------------------------------------------------------------------- ++ // Label iter_le_s_len ++ curr0_opnd = make_label_op(iter_le_s_len); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // %4 = load i64, i64* %iter ++ curr_instr = gen_new_instr(curr_instr, I_LOAD, i64_ty, iter_opnd, NULL, NULL); ++ ++ // %5 = sub nsw i64 %4, 1 ++ curr0_opnd = make_tmp_op(i64_ty, curr_instr->tmps); ++ curr1_opnd = make_constval_op(i64_ty, 1, 0); ++ curr_instr = ++ gen_new_instr(curr_instr, I_SUB, i64_ty, curr0_opnd, curr1_opnd, NULL); ++ curr_instr->flags = NOSIGNEDWRAP; ++ save_tmps_instr = curr_instr; ++ ++ if (mode == 1 || mode == 2) { ++ // %6 = getelementptr i8, i8* s1_instr, i64 %5 ++ curr0_opnd = make_tmp_op(ptr_i8_ty, s1_instr->tmps); ++ curr1_opnd = make_tmp_op(i64_ty, curr_instr->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_GEP, ptr_i8_ty, curr0_opnd, ++ curr1_opnd, NULL); ++ } else if (mode == 3) { ++ // %6 = bitcast [1 x i8]* "space_sym" to i8* ++ curr0_opnd = make_var_op(getstring(" ", 1)); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BITCAST, ptr_i8_ty, curr0_opnd, NULL, NULL); ++ } ++ ++ // %7 = load volatile i8, i8* %6 ++ curr0_opnd = make_tmp_op(ptr_i8_ty, curr_instr->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_LOAD, i8_ty, curr0_opnd, NULL, NULL); ++ curr_instr->flags = LOOP_BACKEDGE_FLAG; ++ ++ // %8 = zext i8 %7 to i32 ++ curr0_opnd = make_tmp_op(i8_ty, curr_instr->tmps); ++ curr_instr = ++ gen_new_instr(curr_instr, I_ZEXT, i32_ty, curr0_opnd, NULL, NULL); ++ cmp1 = curr_instr; ++ ++ if (mode == 1 || mode == 3) { ++ // %9 = getelementptr i8, i8* s2_instr, i64 %5 ++ curr0_opnd = make_tmp_op(ptr_i8_ty, s2_instr->tmps); ++ curr1_opnd = make_tmp_op(i64_ty, save_tmps_instr->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_GEP, ptr_i8_ty, curr0_opnd, ++ curr1_opnd, NULL); ++ } else if (mode == 2) { ++ // %9 = bitcast [1 x i8]* "space_sym" to i8* ++ curr0_opnd = make_var_op(getstring(" ", 1)); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BITCAST, ptr_i8_ty, curr0_opnd, NULL, NULL); ++ } ++ ++ // %10 = load volatile i8, i8* %9 ++ curr0_opnd = make_tmp_op(ptr_i8_ty, curr_instr->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_LOAD, i8_ty, curr0_opnd, NULL, NULL); ++ curr_instr->flags = LOOP_BACKEDGE_FLAG; ++ ++ // %11 = zext i8 %10 to i32 ++ curr0_opnd = make_tmp_op(i8_ty, curr_instr->tmps); ++ curr_instr = ++ gen_new_instr(curr_instr, I_ZEXT, i32_ty, curr0_opnd, NULL, NULL); ++ cmp2 = curr_instr; ++ ++ // %12 = icmp eq i64 %8, %11 ++ curr0_opnd = make_ot_cc_op(OT_EQ); ++ curr1_opnd = make_tmp_op(i32_ty, cmp1->tmps); ++ curr2_opnd = make_tmp_op(i32_ty, cmp2->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++ ++ // br i1 %12, label %loop_s1_eq_s2, label %loop_s1_ne_s2 ++ SPTR loop_s1_eq_s2 = mklabel("loop_s1_eq_s2"); ++ SPTR loop_s1_ne_s2 = mklabel("loop_s1_ne_s2"); ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(loop_s1_eq_s2); ++ curr2_opnd = make_target_op(loop_s1_ne_s2); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // -------------------------------------------------------------------- ++ // Label loop_s1_ne_s2 ++ curr0_opnd = make_label_op(loop_s1_ne_s2); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // %13 = icmp sgt i64 %8, %11 ++ curr0_opnd = make_ot_cc_op(OT_SGT); ++ curr1_opnd = make_tmp_op(i32_ty, cmp1->tmps); ++ curr2_opnd = make_tmp_op(i32_ty, cmp2->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++ ++ // br i1 %13, label %loop_s1_gt_s2, label %loop_s1_lt_s2 ++ SPTR loop_s1_gt_s2 = mklabel("loop_s1_gt_s2"); ++ SPTR loop_s1_lt_s2 = mklabel("loop_s1_lt_s2"); ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(loop_s1_gt_s2); ++ curr2_opnd = make_target_op(loop_s1_lt_s2); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // -------------------------------------------------------------------- ++ // Label loop_s1_gt_s2 ++ curr0_opnd = make_label_op(loop_s1_gt_s2); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // label %result_one ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, result_one_opnd, NULL, NULL); ++ ++ // -------------------------------------------------------------------- ++ // Label loop_s1_lt_s2 ++ curr0_opnd = make_label_op(loop_s1_lt_s2); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // br label %result_minus_one ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, result_minus_one_opnd, NULL, NULL); ++ ++ // -------------------------------------------------------------------- ++ // Label loop_s1_eq_s2 ++ curr0_opnd = make_label_op(loop_s1_eq_s2); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ curr0_opnd = make_constval_op(i64_ty, 1, 0); ++ inc_loop_iter(iter_opnd, i64_ty, curr0_opnd); ++ ++ // br label %loop_header ++ curr0_opnd = make_target_op(loop_header); ++ curr_instr = gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, NULL, NULL); ++ ++ // -------------------------------------------------------------------- ++ // Label iter_gt_s_len ++ curr0_opnd = make_label_op(iter_gt_s_len); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ return curr_instr; ++} ++ ++static INSTR_LIST * ++inline_f90_strcmp_klen() ++{ ++ dbg_line_op = runtime_instr->dbg_line_op; ++ INSTR_LIST *s1_len, *s2_len, *strcmp_res, *iter; ++ INSTR_LIST *head_instr; ++ INSTR_LIST *cmp1, *cmp2, *save_tmps_instr; ++ OPERAND *curr0_opnd, *curr1_opnd, *curr2_opnd, *opnd_zero, *final_opnd; ++ OPERAND *result_zero_opnd, *result_one_opnd, *result_minus_one_opnd; ++ OPERAND *s1_len_opnd, *s2_len_opnd, *strcmp_res_opnd, *iter_opnd; ++ INSTR_LIST *prev_instr = runtime_instr->prev; ++ INSTR_LIST *next_instr = runtime_instr->next; ++ INSTR_LIST *s1_instr = runtime_instr->operands->next->tmps->info.idef; ++ INSTR_LIST *s2_instr = runtime_instr->operands->next->next->tmps->info.idef; ++ TMPS *s1_len_tmps = runtime_instr->operands->next->next->next->tmps; ++ TMPS *s2_len_tmps = runtime_instr->operands->next->next->next->next->tmps; ++ ++ LL_Type *i1_ty = make_int_lltype(1); ++ LL_Type *i64_ty = make_int_lltype(64); ++ LL_Type *ptr_i64_ty = make_ptr_lltype(i64_ty); ++ ++ SPTR final_bb = mklabel("final_bb"); ++ final_opnd = make_target_op(final_bb); ++ SPTR result_zero = mklabel("result_zero"); ++ result_zero_opnd = make_target_op(result_zero); ++ SPTR result_one = mklabel("result_one"); ++ result_one_opnd = make_target_op(result_one); ++ SPTR result_minus_one = mklabel("result_minus_one"); ++ result_minus_one_opnd = make_target_op(result_minus_one); ++ ++ // Allocate space for s1_len, s2_len, strcmp result, loop iterator ++ s1_len = gen_alloca_instr(ptr_i64_ty); ++ s2_len = gen_alloca_instr(ptr_i64_ty); ++ strcmp_res = gen_alloca_instr(ptr_i64_ty); ++ iter = gen_alloca_instr(ptr_i64_ty); ++ // Link allocas instructions ++ link_instrs(s1_len, s2_len); ++ link_instrs(s2_len, strcmp_res); ++ link_instrs(strcmp_res, iter); ++ // Insert allocas ++ link_instrs(iter, top_instr->next); ++ link_instrs(top_instr, s1_len); ++ // Make tmp operands for allocas ++ s1_len_opnd = make_tmp_op(ptr_i64_ty, s1_len->tmps); ++ s2_len_opnd = make_tmp_op(ptr_i64_ty, s2_len->tmps); ++ strcmp_res_opnd = make_tmp_op(ptr_i64_ty, strcmp_res->tmps); ++ iter_opnd = make_tmp_op(ptr_i64_ty, iter->tmps); ++ ++ // First action instruction: store i64 %s1_len_tmps, i64* %s1_len ++ if (s1_len_tmps) ++ curr0_opnd = make_tmp_op(i64_ty, s1_len_tmps); ++ else ++ curr0_opnd = mirror_nontmp_op(runtime_instr->operands->next->next->next); ++ curr0_opnd->next = s1_len_opnd; ++ curr_instr = gen_instr(I_STORE, NULL, NULL, curr0_opnd); ++ head_instr = curr_instr; ++ ++ // store i64 %s2_len_tmps, i64* %s2_len ++ if (s2_len_tmps) ++ curr0_opnd = make_tmp_op(i64_ty, s2_len_tmps); ++ else ++ curr0_opnd = ++ mirror_nontmp_op(runtime_instr->operands->next->next->next->next); ++ curr_instr = gen_new_st(curr_instr, curr0_opnd, s2_len_opnd); ++ ++ process_strlen_lt_zero(s1_len_opnd); ++ process_strlen_lt_zero(s2_len_opnd); ++ ++ // %1 = load i64, i64* %s1_len ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, i64_ty, s1_len_opnd, NULL, NULL); ++ ++ // %2 = icmp ne i64 %1, 0 ++ curr0_opnd = make_ot_cc_op(OT_NE); ++ curr1_opnd = make_tmp_op(i64_ty, curr_instr->tmps); ++ curr2_opnd = make_constval_op(i64_ty, 0, 0); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++ ++ // br i1 %2, label %ahead, label %is_s2_len_zero ++ SPTR ahead = mklabel("ahead"); ++ SPTR is_s2_len_zero = mklabel("is_s2_len_zero"); ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(ahead); ++ curr2_opnd = make_target_op(is_s2_len_zero); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // Label is_s2_len_zero ++ curr0_opnd = make_label_op(is_s2_len_zero); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // %3 = load i64, i64* %s2_len ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, i64_ty, s2_len_opnd, NULL, NULL); ++ ++ // %4 = icmp ne i64 %3, 0 ++ curr0_opnd = make_ot_cc_op(OT_NE); ++ curr1_opnd = make_tmp_op(i64_ty, curr_instr->tmps); ++ curr2_opnd = make_constval_op(i64_ty, 0, 0); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++ ++ // br i1 %4, label %ahead, label %result_zero ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(ahead); ++ curr2_opnd = result_zero_opnd; ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // Label ahead ++ curr0_opnd = make_label_op(ahead); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // -------------------------------------------------------------------- ++ // %5 = load i64, i64* %s1_len ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, i64_ty, s1_len_opnd, NULL, NULL); ++ cmp1 = curr_instr; ++ ++ // %6 = load i64, i64* %s2_len ++ curr_instr = ++ gen_new_instr(curr_instr, I_LOAD, i64_ty, s2_len_opnd, NULL, NULL); ++ cmp2 = curr_instr; ++ ++ // %7 = icmp eq i64 %5, %6 ++ curr0_opnd = make_ot_cc_op(OT_EQ); ++ curr1_opnd = make_tmp_op(i64_ty, cmp1->tmps); ++ curr2_opnd = make_tmp_op(i64_ty, cmp2->tmps); ++ curr_instr = gen_new_instr(curr_instr, I_ICMP, i1_ty, curr0_opnd, curr1_opnd, ++ curr2_opnd); ++ ++ // br i1 %7, label %s1_len_eq_s2_len, label %s1_len_ne_s2_len ++ SPTR s1_len_eq_s2_len = mklabel("s1_len_eq_s2_len"); ++ SPTR s1_len_ne_s2_len = mklabel("s1_len_ne_s2_len"); ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(s1_len_eq_s2_len); ++ curr2_opnd = make_target_op(s1_len_ne_s2_len); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // -------------------------------------------------------------------- ++ // Label s1_len_eq_s2_len ++ curr0_opnd = make_label_op(s1_len_eq_s2_len); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // store i64 1, i64* iter_opnd ++ curr0_opnd = make_constval_op(i64_ty, 1, 0); ++ curr_instr = gen_new_st(curr_instr, curr0_opnd, iter_opnd); ++ ++ curr_instr = loop_s1_cmp_s2(iter_opnd, s1_len_opnd, result_one_opnd, ++ result_minus_one_opnd, 1); ++ ++ // br label %result_zero ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, result_zero_opnd, NULL, NULL); ++ ++ // -------------------------------------------------------------------- ++ // Label s1_len_ne_s2_len ++ curr0_opnd = make_label_op(s1_len_ne_s2_len); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ cmp_two_vars(s1_len_opnd, s2_len_opnd, i64_ty, OT_SGT); ++ ++ // br i1 %8, label %s1_len_gt_s2_len, label %s1_len_le_s2_len ++ SPTR s1_len_gt_s2_len = mklabel("s1_len_gt_s2_len"); ++ SPTR s1_len_lt_s2_len = mklabel("s1_len_lt_s2_len"); ++ curr0_opnd = make_tmp_op(i1_ty, curr_instr->tmps); ++ curr1_opnd = make_target_op(s1_len_gt_s2_len); ++ curr2_opnd = make_target_op(s1_len_lt_s2_len); ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, curr0_opnd, curr1_opnd, curr2_opnd); ++ ++ // -------------------------------------------------------------------- ++ // Label s1_len_gt_s2_len ++ curr0_opnd = make_label_op(s1_len_gt_s2_len); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // store i64 1, i64* iter_opnd ++ curr0_opnd = make_constval_op(i64_ty, 1, 0); ++ curr_instr = gen_new_st(curr_instr, curr0_opnd, iter_opnd); ++ ++ curr_instr = loop_s1_cmp_s2(iter_opnd, s2_len_opnd, result_one_opnd, ++ result_minus_one_opnd, 1); ++ ++ curr_instr = loop_s1_cmp_s2(iter_opnd, s1_len_opnd, result_one_opnd, ++ result_minus_one_opnd, 2); ++ ++ // br label %result_zero ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, result_zero_opnd, NULL, NULL); ++ ++ // -------------------------------------------------------------------- ++ // Label s1_len_lt_s2_len ++ curr0_opnd = make_label_op(s1_len_lt_s2_len); ++ curr_instr = gen_new_instr(curr_instr, I_NONE, NULL, curr0_opnd, NULL, NULL); ++ ++ // store i64 1, i64* iter_opnd ++ curr0_opnd = make_constval_op(i64_ty, 1, 0); ++ curr_instr = gen_new_st(curr_instr, curr0_opnd, iter_opnd); ++ ++ curr_instr = loop_s1_cmp_s2(iter_opnd, s1_len_opnd, result_one_opnd, ++ result_minus_one_opnd, 1); ++ ++ curr_instr = loop_s1_cmp_s2(iter_opnd, s2_len_opnd, result_one_opnd, ++ result_minus_one_opnd, 3); ++ ++ // br label %result_zero ++ curr_instr = ++ gen_new_instr(curr_instr, I_BR, NULL, result_zero_opnd, NULL, NULL); ++ ++ // -------------------------------------------------------------------- ++ // Label %result_zero: store i64 0, i64* strcmp_res ++ curr0_opnd = make_constval_op(i64_ty, 0, 0); ++ store_result_bb(result_zero, curr0_opnd, strcmp_res_opnd, final_opnd); ++ ++ // Label %result_one: store i64 1, i64* strcmp_res ++ curr0_opnd = make_constval_op(i64_ty, 1, 0); ++ store_result_bb(result_one, curr0_opnd, strcmp_res_opnd, final_opnd); ++ ++ // Label %result_minus_one: store i64 -1, i64* strcmp_res ++ curr0_opnd = make_constval_op(i64_ty, -1, 0); ++ store_result_bb(result_minus_one, curr0_opnd, strcmp_res_opnd, final_opnd); ++ ++ // -------------------------------------------------------------------- ++ // Label %final_bb ++ finalize_bb(final_bb, strcmp_res_opnd, i64_ty); ++ ++ mirror_instr(runtime_instr, curr_instr); ++ ++ link_instrs(prev_instr, head_instr); ++ link_instrs(curr_instr->prev, runtime_instr); ++ return runtime_instr; ++} ++ ++INSTR_LIST * ++emit_inline_runtime_call(INSTR_LIST *top, INSTR_LIST *instr, ++ INLINE_RUNTIME_ID inline_runtime_id) ++{ ++ INSTR_LIST *prev_instr = nullptr; ++ top_instr = top; ++ runtime_instr = instr; ++ switch (inline_runtime_id) { ++ case NONE_ID: ++ assert(false, "runtime call is not inlinable", 0, ERR_Fatal); ++ break; ++ case F90_STRCMP_KLEN: ++ prev_instr = inline_f90_strcmp_klen(); ++ break; ++ } ++ ++ return prev_instr; ++} ++ ++static void ++init_inline_runtime_call() ++{ ++ std::string f90_strcmp_klen = "@f90_strcmp_klen"; ++ inline_calls[f90_strcmp_klen] = F90_STRCMP_KLEN; ++} ++ ++INLINE_RUNTIME_ID ++get_inline_runtime_id(INSTR_LIST *instr) ++{ ++ assert(instr->i_name == I_CALL, "instr must be call instruction", 0, ++ ERR_Fatal); ++ ++ if (!is_initialized_inline_calls) { ++ init_inline_runtime_call(); ++ is_initialized_inline_calls = true; ++ } ++ ++ if (instr->operands) { ++ OPERAND *call_name_op = instr->operands; ++ if (call_name_op->ot_type == OT_TMP && call_name_op->tmps && ++ call_name_op->tmps->info.idef) { ++ INSTR_LIST *call_iface_instr = call_name_op->tmps->info.idef; ++ if (call_iface_instr->i_name == I_BITCAST && call_iface_instr->operands) { ++ char *call_name = call_iface_instr->operands->string; ++ if (call_name && ++ inline_calls.find(std::string(call_name)) != inline_calls.end()) ++ return inline_calls[std::string(call_name)]; ++ } ++ } ++ } ++ return NONE_ID; ++} +diff --git a/tools/flang2/flang2exe/inline_runtime.h b/tools/flang2/flang2exe/inline_runtime.h +new file mode 100644 +index 00000000..79ca0a66 +--- /dev/null ++++ b/tools/flang2/flang2exe/inline_runtime.h +@@ -0,0 +1,25 @@ ++/* ++ * 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 INLINE_RUNTIME_H_ ++#define INLINE_RUNTIME_H_ ++ ++#include "gbldefs.h" ++#include "global.h" ++#include "llutil.h" ++#include "symtab.h" ++ ++enum INLINE_RUNTIME_ID { ++ NONE_ID = 0, ++ F90_STRCMP_KLEN, ++}; ++ ++INLINE_RUNTIME_ID get_inline_runtime_id(INSTR_LIST *instrs); ++INSTR_LIST *emit_inline_runtime_call(INSTR_LIST *, INSTR_LIST *, ++ INLINE_RUNTIME_ID); ++ ++#endif +diff --git a/tools/flang2/flang2exe/llassem.cpp b/tools/flang2/flang2exe/llassem.cpp +index ef0440eb..7153a331 100644 +--- a/tools/flang2/flang2exe/llassem.cpp ++++ b/tools/flang2/flang2exe/llassem.cpp +@@ -1224,6 +1224,7 @@ write_consts(void) + { + if (gbl.consts > NOSYM) { + SPTR sptr; ++ SPTR space_sptr = getstring(" ", 1); + for (sptr = gbl.consts; sptr > NOSYM; sptr = SYMLKG(sptr)) { + DTYPE dtype = DTYPEG(sptr); + if (DTY(dtype) == TY_CHAR) { +@@ -1249,6 +1250,21 @@ write_consts(void) + } + fputc('\n', ASMFIL); + } ++ /* Mark the space_sptr as NME_NULL when the space symbol is already ++ * written out if the program uses the space symbol. ++ */ ++ if (XBIT(218, 0x1) && sptr == space_sptr) ++ space_sptr = SPTR_NULL; ++ } ++ if (XBIT(218, 0x1) && space_sptr != SPTR_NULL) { ++ /* Write out the space symbol if it is not written out. The space symbol ++ * is used for inlining Fortran runtime calls enabled by XBIT(218, 0x1). ++ * Specifically, for string comparision which uses f90_strcmp_klen call, ++ * the string element is taken as space symbol if not assigned one value ++ * in user code (see function loop_s1_cmp_s2 in inline_runtime.cpp). ++ */ ++ put_fstr(space_sptr, XBIT(124, 0x8000)); ++ fputc('\n', ASMFIL); + } + if (flg.smp || XBIT(34, 0x200 || gbl.usekmpc)) { + SPTR tsptr = SPTR_NULL; +@@ -1260,6 +1276,11 @@ write_consts(void) + if (tsptr) + SYMLKP(tsptr, SPTR_NULL); + } ++ } else if (XBIT(218, 0x1)) { ++ /* Write out the space symbol if there is no const in current procedure. */ ++ SPTR space_sptr = getstring(" ", 1); ++ put_fstr(space_sptr, XBIT(124, 0x8000)); ++ fputc('\n', ASMFIL); + } + gbl.consts = NOSYM; + } diff --git a/flang.spec b/flang.spec index ce54cae..822d39a 100644 --- a/flang.spec +++ b/flang.spec @@ -2,7 +2,7 @@ Name: flang Version: flang_20210324 -Release: 3 +Release: 4 Summary: Fortran language compiler targeting LLVM License: Apache-2.0 @@ -32,6 +32,9 @@ TODO: support build Flang. %changelog +* Thu Jul 14 2022 qiaopeixin - flang_20210324-4 +- Add patch for inline of runtime functions + * Wed May 18 2022 liukuo - flang_20210324-3 - License compliance rectification -- Gitee