-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #110 from julelang/boundary
compiler: improve boundary analysis and optimizations
- Loading branch information
Showing
6 changed files
with
365 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// Copyright 2024 The Jule Programming Language. | ||
// Use of this source code is governed by a BSD 3-Clause | ||
// license that can be found in the LICENSE file. | ||
|
||
use std::jule::constant::{Const} | ||
use std::jule::lex::{TokenId} | ||
use std::jule::sema::{Var, StructSubIdentExprModel, UnaryExprModel, ExprModel, TypeKind} | ||
|
||
const invalidBoundary = 0x0 | ||
|
||
struct boundaryVar { | ||
var: uintptr | ||
maxSize: []ExprModel | ||
} | ||
|
||
// Information wrapper for boundary analysis. | ||
struct boundary { | ||
vars: []&boundaryVar | ||
} | ||
|
||
impl boundary { | ||
// Appends variable with initial maximum size expression. | ||
// If variable is already exist, updates maximum size information. | ||
fn pushVar(mut self, var: uintptr, mut maxSize: ExprModel) { | ||
if !Access || var == invalidBoundary { | ||
// Ignore it, because this optimizations within scope of the --opt-access flag. | ||
ret | ||
} | ||
if !isValidBoundaryInfo(maxSize) { | ||
ret | ||
} | ||
for (_, mut v) in self.vars { | ||
if v.var == var { | ||
for i, max in v.maxSize { | ||
if fitsSize(maxSize, max) { | ||
// Maximum size is fits, so new size is larger than current size. | ||
v.maxSize[i] = maxSize | ||
ret | ||
} | ||
} | ||
v.maxSize = append(v.maxSize, maxSize) | ||
ret | ||
} | ||
} | ||
// Not exist, append new one. | ||
for (_, mut v) in self.vars { | ||
if v.var == invalidBoundary { | ||
// Empty place, use here instead of append. | ||
v.var = var | ||
v.maxSize = append(v.maxSize, maxSize) | ||
ret | ||
} | ||
} | ||
self.vars = append(self.vars, &boundaryVar{var: var, maxSize: [maxSize]}) | ||
} | ||
|
||
fn removeVar(mut self, var: uintptr): bool { | ||
if var != invalidBoundary { | ||
for (_, mut v) in self.vars { | ||
if v.var == var { | ||
v.var = invalidBoundary | ||
v.maxSize = v.maxSize[:0] | ||
ret true | ||
} | ||
} | ||
} | ||
ret false | ||
} | ||
|
||
// Reports whether maximum size of variable is fits with given expression. | ||
fn fitsMaxSize(mut self, var: uintptr, expr: ExprModel): bool { | ||
if var != invalidBoundary { | ||
for _, v in self.vars { | ||
if v.var == var { | ||
ret fitsMaxSize(v.maxSize, expr) != -1 | ||
} | ||
} | ||
} | ||
ret false | ||
} | ||
} | ||
|
||
// Reports whether model is valid maximum size information for boundary analysis. | ||
fn isValidBoundaryInfo(m: ExprModel): bool { ret fitsSize(m, m) } | ||
|
||
// Reports whether maximum size is fits with given expression. | ||
// In other words, reports whether: max >= expr | ||
// Returns index number of max size which is fit, otherwise -1. | ||
fn fitsMaxSize(max: []ExprModel, expr: ExprModel): int { | ||
for i, m in max { | ||
if fitsSize(m, expr) { | ||
ret i | ||
} | ||
} | ||
ret -1 | ||
} | ||
|
||
fn fitsSize(e1: ExprModel, e2: ExprModel): bool { | ||
if typeData(e1) != typeData(e2) { | ||
ret false | ||
} | ||
match type e1 { | ||
| &Const: | ||
exprConst := (&Const)(e2) | ||
ret exprConst.AsF64() >= 0 && (&Const)(e1).GtEq(*exprConst) | ||
| &Var: | ||
ret e1 == e2 | ||
| &StructSubIdentExprModel: | ||
ssi1 := (&StructSubIdentExprModel)(e1) | ||
ssi2 := (&StructSubIdentExprModel)(e2) | ||
ret equalModels(ssi1.Expr.Model, ssi2.Expr.Model) && ssi1.Field == ssi2.Field | ||
| &UnaryExprModel: | ||
uem1 := (&UnaryExprModel)(e1) | ||
uem2 := (&UnaryExprModel)(e2) | ||
if uem1.Op.Id != TokenId.Star || uem1.Op.Id != uem2.Op.Id { | ||
ret false | ||
} | ||
ret fitsSize(uem1.Expr.Model, uem2.Expr.Model) | ||
|: | ||
ret false | ||
} | ||
} | ||
|
||
fn possibleBoundaryRemove1(mut &b: &boundary, model: ExprModel) { | ||
if b != nil { | ||
b.removeVar(getBoundaryVar(model)) | ||
} | ||
} | ||
|
||
fn possibleBoundaryRemove2(mut &b: &boundary, model: ExprModel) { | ||
if b == nil { | ||
ret | ||
} | ||
// Clear if size data mutating. | ||
for (_, mut v) in b.vars { | ||
for i, max in v.maxSize { | ||
if equalModels(max, model) { | ||
v.maxSize = append(v.maxSize[:i], v.maxSize[i+1:]...) | ||
break | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn possibleBoundaryRemove(mut &b: &boundary, model: ExprModel) { | ||
if b == nil { | ||
ret | ||
} | ||
if b.removeVar(getBoundaryVar(model)) { | ||
// Exist as a variable, so cannot be size data. | ||
// Remove variable and return. | ||
ret | ||
} | ||
// Clear if size data mutating. | ||
for (_, mut v) in b.vars { | ||
for i, max in v.maxSize { | ||
if equalModels(max, model) { | ||
v.maxSize = append(v.maxSize[:i], v.maxSize[i+1:]...) | ||
break | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn isBoundaryRiskyType(mut t: &TypeKind): bool { ret t.Sptr() != nil } | ||
fn isBoundaryValidType(mut t: &TypeKind): bool { ret t.Slc() != nil || t.Arr() != nil } | ||
|
||
fn getBoundaryVar(m: ExprModel): uintptr { | ||
if !Access { | ||
ret invalidBoundary | ||
} | ||
match type m { | ||
| &Var: | ||
v := (&Var)(m) | ||
if !v.Reference { | ||
// Variable is not reference, return address of it. | ||
ret uintptr((&Var)(m)) | ||
} | ||
// Variable is reference, it should be initialized at source code. | ||
// Investigate the initial expression for variable address. | ||
ret getBoundaryVar(v.Value.Data.Model) | ||
| &StructSubIdentExprModel: | ||
ret uintptr((&StructSubIdentExprModel)(m).Field) | ||
| &UnaryExprModel: | ||
uem := (&UnaryExprModel)(m) | ||
if uem.Op.Id == TokenId.Star { // Dereferencing. | ||
ret getBoundaryVar(uem.Expr.Model) | ||
} | ||
} | ||
ret invalidBoundary | ||
} |
Oops, something went wrong.