From ba62878d5528fc5f8326ecc0093c80ea4f59fe29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ada=20Gottenstr=C3=A4ter?= Date: Sat, 1 Jul 2023 20:52:31 +0200 Subject: [PATCH 1/2] P2 bit op constant evaluation --- backends/asm/optimize_ir.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backends/asm/optimize_ir.c b/backends/asm/optimize_ir.c index fcdaf10dd..d10b34616 100644 --- a/backends/asm/optimize_ir.c +++ b/backends/asm/optimize_ir.c @@ -1938,6 +1938,15 @@ TransformConstDst(IR *ir, Operand *imm) // Z is set if bit is SET val1 = ~val1 & (1<<(val2&31)); break; + case OPC_BITH: + val1 |= EvalP2BitMask(val2); + break; + case OPC_BITL: + val1 &= ~EvalP2BitMask(val2); + break; + case OPC_BITNOT: + val1 ^= EvalP2BitMask(val2); + break; default: return 0; } From dfe50507fad7eef0f72089adc13039177b358f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ada=20Gottenstr=C3=A4ter?= Date: Sat, 1 Jul 2023 20:53:41 +0200 Subject: [PATCH 2/2] Inline pure functions when all args are constant --- backends/asm/optimize_ir.c | 81 ++++++++++++++++++++++++++++++-------- backends/asm/outasm.c | 13 ++---- backends/asm/outasm.h | 9 +++-- 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/backends/asm/optimize_ir.c b/backends/asm/optimize_ir.c index d10b34616..1d0fdd9b2 100644 --- a/backends/asm/optimize_ir.c +++ b/backends/asm/optimize_ir.c @@ -1074,6 +1074,28 @@ DoReorderBlock(IRList *irl,IR *after,IR *top,IR *bottom) { after->next = top; } +// Return true for "normal" ALU ops +static bool +IsSafeALUOp(IROpcode opc) { + switch(opc) { + case OPC_MOV: + case OPC_ADD: + case OPC_SUB: + case OPC_AND: + case OPC_OR: + case OPC_XOR: + case OPC_TEST: + case OPC_TESTN: + case OPC_SHL: + case OPC_SHR: + case OPC_NEG: + case OPC_ABS: + return true; + default: + return false; + } +} + /* * return TRUE if the operand's value does not need to be preserved * after instruction instr @@ -1113,18 +1135,10 @@ doIsDeadAfter(IR *instr, Operand *op, int level, IR **stack) if (InstrSetsAnyFlags(ir)) { return false; // flag setting matters, we are not dead } - switch(ir->opc) { // be very cautious about whether op is dead if // any "unusal" opcodes (like waitpeq) are used - // so just accept - case OPC_ADD: - case OPC_SUB: - case OPC_AND: - case OPC_OR: - case OPC_XOR: - // not definitely alive or dead yet - break; - default: + // so just accept a safe subset + if (!IsSafeALUOp(ir->opc)) { // assume live return false; } @@ -5230,17 +5244,22 @@ NeverInline(Function *f) // // check a function to see if it should be inlined +// and return true if new inlining opportunites were unlocked // #define INLINE_THRESHOLD_P1 2 #define INLINE_THRESHOLD_P2 4 bool -ShouldBeInlined(Function *f) +AnalyzeInlineEligibility(Function *f) { IR *ir; int n = 0; int paramfactor; int threshold; + bool pure = true; + + unsigned prev_flags = FuncData(f)->inliningFlags; + FuncData(f)->inliningFlags = 0; if (f->prefer_inline) { threshold = 100; @@ -5278,15 +5297,25 @@ ShouldBeInlined(Function *f) } } + // Check for impurity here. This check is very conservative. + if (!IsLabel(ir) && !IsSafeALUOp(ir->opc)) pure = false; + if (ir->dst && !(IsLocalOrArg(ir->dst) || isResult(ir->dst))) pure = false; + if (ir->src && !(IsImmediate(ir->src) || IsLocalOrArg(ir->src) || isResult(ir->src))) pure = false; + n++; } + + if (pure) { + FuncData(f)->inliningFlags |= ASM_INLINE_PURE_FLAG; + } + // a function called from only 1 place should be inlined // if it means that the function definition can be eliminated if (RemoveIfInlined(f) && (gl_optimize_flags & OPT_INLINE_SINGLEUSE)) { if (f->callSites == 1) { - return true; - } else if (f->callSites == 2) { - return (n <= 2*threshold); + FuncData(f)->inliningFlags |= ASM_INLINE_SINGLE_FLAG; + } else if (f->callSites == 2 && (n <= 2*threshold)) { + FuncData(f)->inliningFlags |= ASM_INLINE_SINGLE_FLAG; } } @@ -5301,13 +5330,33 @@ ShouldBeInlined(Function *f) } else { paramfactor = f->numparams; } - return n <= (threshold + paramfactor); + if (n <= (threshold + paramfactor)) { + FuncData(f)->inliningFlags |= ASM_INLINE_SMALL_FLAG; + } + + return FuncData(f)->inliningFlags & ~prev_flags; } static inline void updateMax(int *dst, int src) { if (*dst < src) *dst = src; } +static bool +ShouldExpandPureFunction(IR *ir) { + Function *f = (Function *)ir->aux; + if (!(FuncData(f)->inliningFlags & ASM_INLINE_PURE_FLAG)) return false; + if (!(gl_optimize_flags & OPT_EXPERIMENTAL)) return false; + if (f->numparams <= 0) return false; + + // Make sure all args are constants + for (int i = 0; i < f->numparams; i++) { + IR *prev_ir = FindPrevSetterForReplace(ir,GetArgReg(i)); + if (!prev_ir || !isConstMove(prev_ir,NULL)) return false; + } + + return true; +} + // // expand function calls inline if appropriate // returns 1 if anything was expanded @@ -5324,7 +5373,7 @@ ExpandInlines(IRList *irl) ir_next = ir->next; if (ir->opc == OPC_CALL) { f = (Function *)ir->aux; - if (f && FuncData(f)->isInline) { + if (f && ((FuncData(f)->inliningFlags & (ASM_INLINE_SMALL_FLAG|ASM_INLINE_SINGLE_FLAG)) || ShouldExpandPureFunction(ir))) { ReplaceIRWithInline(irl, ir, f); FuncData(f)->actual_callsites--; updateMax(&FuncData(curfunc)->maxInlineArg,f->numparams); diff --git a/backends/asm/outasm.c b/backends/asm/outasm.c index 793c69903..34726880d 100644 --- a/backends/asm/outasm.c +++ b/backends/asm/outasm.c @@ -5605,7 +5605,7 @@ RemoveIfInlined(Function *f) static bool ActuallyInlined(Function *f) { - if (!FuncData(f)->isInline) { + if (FuncData(f)->inliningFlags == 0) { return false; } if (FuncData(f)->actual_callsites > 0) { @@ -5754,7 +5754,7 @@ CompileFunc_internal(void *vptr, Module *P) continue; curfunc = f; CompileFunctionBody(f); - FuncData(f)->isInline = ShouldBeInlined(f); + AnalyzeInlineEligibility(f); } curfunc = savecurf; return 0; @@ -5766,7 +5766,6 @@ ExpandInline_internal(void *vptr, Module *P) Function *f; int change; int newInlines; - int inlineStatus; int anyChange = 0; do { @@ -5783,12 +5782,8 @@ ExpandInline_internal(void *vptr, Module *P) OptimizeIRLocal(firl, f); // revisit the question of whether it should be inlined, given that // we've perhaps changed its size - if (!FuncData(f)->isInline) { - inlineStatus = ShouldBeInlined(f); - if (inlineStatus) { - newInlines++; - FuncData(f)->isInline = true; - } + if (AnalyzeInlineEligibility(f)) { + newInlines++; } } } diff --git a/backends/asm/outasm.h b/backends/asm/outasm.h index d50f6bbb7..735711be1 100644 --- a/backends/asm/outasm.h +++ b/backends/asm/outasm.h @@ -61,7 +61,7 @@ IR *EmitLabel(IRList *list, Operand *op); void OptimizeIRLocal(IRList *irl, Function *f); void OptimizeIRGlobal(IRList *irl); void OptimizeFcache(IRList *irl); -bool ShouldBeInlined(Function *f); +bool AnalyzeInlineEligibility(Function *f); bool RemoveIfInlined(Function *f); int ExpandInlines(IRList *irl); @@ -121,8 +121,11 @@ typedef struct ir_bedata { /* number of local registers that need to be pushed */ int numsavedregs; - /* flag for whether we should inline the function */ - bool isInline; + /* flags for whether we should inline the function */ + unsigned inliningFlags; + #define ASM_INLINE_SMALL_FLAG 0x01 + #define ASM_INLINE_SINGLE_FLAG 0x02 + #define ASM_INLINE_PURE_FLAG 0x04 /* set after inlining if the function has no external calls left */ bool effectivelyLeaf;