Skip to content

Commit

Permalink
compiler: improve the --opt-str optimization flag for the string conc…
Browse files Browse the repository at this point in the history
…atenations
  • Loading branch information
mertcandav committed Oct 17, 2024
1 parent 03f500e commit 4ea2a9c
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 3 deletions.
25 changes: 25 additions & 0 deletions src/julec/obj/cxx/expr.jule
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum compExprModel: type {
&opt::UnsafeDerefExprModel,
&opt::UnsafeCastingExprModel,
&opt::FnCallIgnoreExceptionalExprModel,
&opt::StrConcatExprModel,
}

struct exprCoder {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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("<unimplemented_expression_model>")
}
Expand Down
66 changes: 63 additions & 3 deletions src/julec/opt/expr.jule
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -361,8 +420,9 @@ impl exprOptimizer {
}

if Str {
if self.strCond(m) {
ret
match {
| self.strCond(m)
| self.strConcat(m):
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/julec/opt/model.jule
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ struct FnCallIgnoreExceptionalExprModel {
Base: &sema::FnCallExprModel
}

struct StrConcatExprModel {
Parts: []sema::ExprModel
}

struct SwapExprModel {
Left: &sema::Data
Right: &sema::Data
Expand Down

0 comments on commit 4ea2a9c

Please sign in to comment.