From 4ea2a9c459134301dac3456ef38f17acca2326d5 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Thu, 17 Oct 2024 19:40:25 +0300 Subject: [PATCH] compiler: improve the --opt-str optimization flag for the string concatenations --- src/julec/obj/cxx/expr.jule | 25 ++++++++++++++ src/julec/opt/expr.jule | 66 +++++++++++++++++++++++++++++++++++-- src/julec/opt/model.jule | 4 +++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/julec/obj/cxx/expr.jule b/src/julec/obj/cxx/expr.jule index f662408d7..41e294e15 100644 --- a/src/julec/obj/cxx/expr.jule +++ b/src/julec/obj/cxx/expr.jule @@ -34,6 +34,7 @@ enum compExprModel: type { &opt::UnsafeDerefExprModel, &opt::UnsafeCastingExprModel, &opt::FnCallIgnoreExceptionalExprModel, + &opt::StrConcatExprModel, } struct exprCoder { @@ -1653,6 +1654,28 @@ impl exprCoder { } } + fn strConcat(mut &self, mut m: &opt::StrConcatExprModel) { + self.oc.write("({ " + typeCoder.Int + " size = 0; ") + for i, p in m.Parts { + ident := "part" + conv::Itoa(i) + self.oc.write(typeCoder.Str + " ") + self.oc.write(ident) + self.oc.write(" = std::move(") + self.possibleRefExpr(p) + self.oc.write("); size += ") + self.oc.write(ident) + self.oc.write(".len(); ") + } + self.oc.write("jule::Slice<" + typeCoder.U8 + "> buf; buf.alloc_new(0, size); ") + for i in m.Parts { + ident := "part" + conv::Itoa(i) + self.oc.write("buf.append(") + self.oc.write(ident) + self.oc.write("); ") + } + self.oc.write(typeCoder.Str + " result; result._len = buf._len; result.buffer = std::move(buf.data); result._slice = buf._slice; std::move(result); })") + } + fn model(mut &self, mut m: compExprModel) { match type m { | str: @@ -1749,6 +1772,8 @@ impl exprCoder { self.unsafeCasting((&opt::UnsafeCastingExprModel)(m)) | &opt::FnCallIgnoreExceptionalExprModel: self.funcCall((&opt::FnCallIgnoreExceptionalExprModel)(m).Base, true) + | &opt::StrConcatExprModel: + self.strConcat((&opt::StrConcatExprModel)(m)) |: self.oc.write("") } diff --git a/src/julec/opt/expr.jule b/src/julec/opt/expr.jule index 93aa4bc22..dd588ef15 100644 --- a/src/julec/opt/expr.jule +++ b/src/julec/opt/expr.jule @@ -129,7 +129,7 @@ impl exprOptimizer { ret true } - fn strCond(self, mut m: &sema::BinaryExprModel): bool { + fn strCond(self, mut &m: &sema::BinaryExprModel): bool { lp := m.Left.Type.Prim() if lp == nil || !lp.IsStr() { ret false @@ -186,6 +186,65 @@ impl exprOptimizer { ret true } + // Tries to optimize string concatenations and reports whether applied. + fn strConcat(self, mut &m: &sema::BinaryExprModel): bool { + lp := m.Left.Type.Prim() + if lp == nil || !lp.IsStr() { + ret false + } + if m.Op.Id != token::Id.Plus { + ret false + } + + // Check the left operand of the binary expression to determine we need to optimize it specifically. + // Use the left perand because of CAST structure. + // See the variable step and function body documentation about binary expressions. + match type m.Left.Model { + | &sema::BinaryExprModel: + // the left operand expression is binary expression + // the expression is like: "x + y + ..." + // we can optimize it + break + |: + // expression is like: "x + y" + // there is no need to optimize it + ret false + } + + mut model := new(StrConcatExprModel) + let mut step: fn(m: sema::ExprModel) + // Point to the step variable in the closure. Otherwise it will be copied, + // so the step function remains as nil, which is causes nil dereferencing. + &_step := step + step = fn(m: sema::ExprModel) { + match type m { + | &sema::BinaryExprModel: + // First, handle the left operand. + // Parts should be in same addition order of the source code. + // By operator precedence and CAST production, + // the first node of the CAST is always the last binary expression. + // For exampple: + // _ = "foo" + x + "bar" + "baz" + // ^ + // In the expression above, the first binary expression node will be pointed one. + // So handle `"foo" + x + "bar"` expression first, then the right operand `+ "baz"`. + // So, recursively handle the left binary expression operands, + // and push the expression models by left-to-right order. + unsafe { _step((&sema::BinaryExprModel)(m).Left.Model) } + unsafe { _step((&sema::BinaryExprModel)(m).Right.Model) } + |: + model.Parts = append(model.Parts, m) + } + } + // Start handling from the last binary expression m. + // See documentation of the step function about binary expressions. + step(m) + + mut anyModel := any(model) + *self.model = unsafe { *(*sema::ExprModel)(&anyModel) } + ret true + } + fn tryNeutralElement1(self, mut &m: &sema::BinaryExprModel, mut c: &constant::Const, mut &nc: &sema::OperandExprModel): bool { if c.IsStr() { // Constant is string, check for string optimizations. if !Str { @@ -361,8 +420,9 @@ impl exprOptimizer { } if Str { - if self.strCond(m) { - ret + match { + | self.strCond(m) + | self.strConcat(m): } } diff --git a/src/julec/opt/model.jule b/src/julec/opt/model.jule index 980879830..f18288501 100644 --- a/src/julec/opt/model.jule +++ b/src/julec/opt/model.jule @@ -14,6 +14,10 @@ struct FnCallIgnoreExceptionalExprModel { Base: &sema::FnCallExprModel } +struct StrConcatExprModel { + Parts: []sema::ExprModel +} + struct SwapExprModel { Left: &sema::Data Right: &sema::Data