Commit dd891640 authored by Roman Alifanov's avatar Roman Alifanov

Optimize codegen: eliminate subshell forks, inline dict/fs methods

- Comparisons set __CT_RET directly instead of $() subshell fork - Logical NOT (!) inlined same way - fs.exists() inlined to [[ -e ]] in conditions - fs.read uses $(<file) instead of $(cat), fs.write/append use printf - fs.exists/remove/mkdir avoid unnecessary $() wrapping - Dict .get()/.has()/.len() inlined for local dict vars - __ct_print simplified: removed unnecessary local variable - String concat ..= uses native += append - Skip __CT_RET self-assignment in return statements
parent 3d28f1b4
...@@ -221,11 +221,6 @@ def _interp(node: IRExpr, ctx: 'EmitContext') -> str: ...@@ -221,11 +221,6 @@ def _interp(node: IRExpr, ctx: 'EmitContext') -> str:
lv = _interp_arith(node.left, ctx) lv = _interp_arith(node.left, ctx)
rv = _interp_arith(node.right, ctx) rv = _interp_arith(node.right, ctx)
return f'$(({lv} {node.operator} {rv}))' return f'$(({lv} {node.operator} {rv}))'
# comparison: emit as $([[ ]] && echo true || echo false)
lb = _expr(node.left, ctx)
rb = _expr(node.right, ctx)
op = node.operator
return f'$([[ {lb} {op} {rb} ]] && echo true || echo false)'
if isinstance(node, (IRMethodCall, IRCall)): if isinstance(node, (IRMethodCall, IRCall)):
inner = _expr(node, ctx) inner = _expr(node, ctx)
if inner.startswith('$(') and inner.endswith(')'): if inner.startswith('$(') and inner.endswith(')'):
...@@ -369,7 +364,7 @@ def _binary(node: IRBinaryOp, ctx: 'EmitContext') -> str: ...@@ -369,7 +364,7 @@ def _binary(node: IRBinaryOp, ctx: 'EmitContext') -> str:
return f'"$(echo "" | awk "BEGIN{{printf \\"%g\\", {la} / {ra}}}")"' return f'"$(echo "" | awk "BEGIN{{printf \\"%g\\", {la} / {ra}}}")"'
return f'"$(({la} {op} {ra}))"' return f'"$(({la} {op} {ra}))"'
# Comparison # Comparison — set __CT_RET directly instead of $() subshell
if op in _CMP_OPS: if op in _CMP_OPS:
is_numeric = lt.kind in ('int', 'float') or rt.kind in ('int', 'float') is_numeric = lt.kind in ('int', 'float') or rt.kind in ('int', 'float')
is_string = lt.kind == 'string' and rt.kind == 'string' is_string = lt.kind == 'string' and rt.kind == 'string'
...@@ -377,20 +372,25 @@ def _binary(node: IRBinaryOp, ctx: 'EmitContext') -> str: ...@@ -377,20 +372,25 @@ def _binary(node: IRBinaryOp, ctx: 'EmitContext') -> str:
la = _to_arith(lv) la = _to_arith(lv)
ra = _to_arith(rv) ra = _to_arith(rv)
arith_op = _ARITH_CMP[op] arith_op = _ARITH_CMP[op]
return f'$([[ $(({la} {arith_op} {ra})) -ne 0 ]] && echo true || echo false)' ctx.emit(f'(( {la} {arith_op} {ra} )) && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
if is_numeric: if is_numeric:
la = _to_arith(lv) la = _to_arith(lv)
ra = _to_arith(rv) ra = _to_arith(rv)
arith_op = _ARITH_CMP[op] arith_op = _ARITH_CMP[op]
return f'$([[ $(({la} {arith_op} {ra})) -ne 0 ]] && echo true || echo false)' ctx.emit(f'(( {la} {arith_op} {ra} )) && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
str_op = _STR_CMP.get(op, op) str_op = _STR_CMP.get(op, op)
return f'$([[ {lv} {str_op} {rv} ]] && echo true || echo false)' ctx.emit(f'[[ {lv} {str_op} {rv} ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
# Logical # Logical
if op == '&&': if op == '&&':
return f'$([[ {lv} == "true" && {rv} == "true" ]] && echo true || echo false)' ctx.emit(f'[[ {lv} == "true" && {rv} == "true" ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
if op == '||': if op == '||':
return f'$([[ {lv} == "true" || {rv} == "true" ]] && echo true || echo false)' ctx.emit(f'[[ {lv} == "true" || {rv} == "true" ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
return f'"{lv} {op} {rv}"' return f'"{lv} {op} {rv}"'
...@@ -519,7 +519,8 @@ def _unary(node: IRUnaryOp, ctx: 'EmitContext') -> str: ...@@ -519,7 +519,8 @@ def _unary(node: IRUnaryOp, ctx: 'EmitContext') -> str:
operand = _expr(node.operand, ctx) operand = _expr(node.operand, ctx)
op = node.operator op = node.operator
if op == '!': if op == '!':
return f'$([[ {operand} == "true" ]] && echo false || echo true)' ctx.emit(f'[[ {operand} == "true" ]] && {RET_VAR}=false || {RET_VAR}=true')
return f'"${{{RET_VAR}}}"'
if op == '-': if op == '-':
inner = _to_arith(operand) inner = _to_arith(operand)
return f'"$((-{inner}))"' return f'"$((-{inner}))"'
...@@ -759,7 +760,7 @@ def _try_inline_string_method(recv_name: str, method: str, args: list, ctx: 'Emi ...@@ -759,7 +760,7 @@ def _try_inline_string_method(recv_name: str, method: str, args: list, ctx: 'Emi
tmp_r = ctx.fresh_tmp() tmp_r = ctx.fresh_tmp()
ctx.emit(f'{tmp_p}={old_arg}') ctx.emit(f'{tmp_p}={old_arg}')
ctx.emit(f'{tmp_r}={new_arg}') ctx.emit(f'{tmp_r}={new_arg}')
ctx.emit(f'{RET_VAR}="${{{recv_name}//"${{{tmp_p}}}"/"{{{tmp_r}}}"}}"') ctx.emit(f'{RET_VAR}="${{{recv_name}//"${{{tmp_p}}}"/"${{{tmp_r}}}"}}"')
return f'"${{{RET_VAR}}}"' return f'"${{{RET_VAR}}}"'
if method == 'contains': if method == 'contains':
...@@ -866,6 +867,17 @@ def _stdlib_method_expr(node: IRMethodCall, ctx: 'EmitContext') -> str: ...@@ -866,6 +867,17 @@ def _stdlib_method_expr(node: IRMethodCall, ctx: 'EmitContext') -> str:
# Dict path # Dict path
if is_known_dict or is_actual_dict or is_param_dict or is_field_dict: if is_known_dict or is_actual_dict or is_param_dict or is_field_dict:
# Fast path: inline dict methods for local dict vars (not params/fields)
if (is_actual_dict or is_known_dict) and not is_field_recv and not is_param_dict:
if method == 'get' and node.args:
key = _expr(node.args[0], ctx)
return f'"${{{recv_name}[{key}]}}"'
if method == 'has' and node.args:
key = _expr(node.args[0], ctx)
ctx.emit(f'[[ -v "{recv_name}[{key}]" ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
if method == 'len':
return f'"${{#{recv_name}[@]}}"'
if is_param_dict or is_field_dict: if is_param_dict or is_field_dict:
dict_ref = recv dict_ref = recv
else: else:
......
...@@ -239,8 +239,7 @@ def _assign(node: IRAssign, ctx: 'EmitContext') -> None: ...@@ -239,8 +239,7 @@ def _assign(node: IRAssign, ctx: 'EmitContext') -> None:
# String concatenation assignment # String concatenation assignment
if op == '..=': if op == '..=':
val = _expr(value, ctx) val = _expr(value, ctx)
val_inner = val.strip('"') ctx.emit(f'{target}+={val}')
ctx.emit(f'{target}="${{{target}}}{val_inner}"')
return return
# Augmented assignment with arithmetic # Augmented assignment with arithmetic
...@@ -355,7 +354,8 @@ def _return(node: IRReturn, ctx: 'EmitContext') -> None: ...@@ -355,7 +354,8 @@ def _return(node: IRReturn, ctx: 'EmitContext') -> None:
ctx.emit(f'{RET_ARR}=("${{{RET_ARR}[@]}}")') ctx.emit(f'{RET_ARR}=("${{{RET_ARR}[@]}}")')
ctx.emit('return 0') ctx.emit('return 0')
return return
ctx.emit(f'{RET_VAR}={val}') if val != f'"${{{RET_VAR}}}"':
ctx.emit(f'{RET_VAR}={val}')
ctx.emit('return 0') ctx.emit('return 0')
...@@ -421,6 +421,14 @@ def _condition_bash(cond, ctx: 'EmitContext') -> str: ...@@ -421,6 +421,14 @@ def _condition_bash(cond, ctx: 'EmitContext') -> str:
inner = _condition_bash(cond.operand, ctx) inner = _condition_bash(cond.operand, ctx)
return f'! ( {inner} )' return f'! ( {inner} )'
# Inline fs.exists(path) → [[ -e path ]]
from ...ir.nodes import IRMethodCall as _IRMCall
if (isinstance(cond, _IRMCall) and isinstance(cond.receiver, IRIdentifier)
and cond.receiver.name == 'fs' and cond.method_name == 'exists'
and cond.args):
path = expr_(cond.args[0], ctx)
return f'[[ -e {path} ]]'
# Variable or call that returns "true"/"false" # Variable or call that returns "true"/"false"
bash = expr_(cond, ctx) bash = expr_(cond, ctx)
if bash == '"true"': if bash == '"true"':
......
...@@ -5,7 +5,7 @@ class CoreFunctions: ...@@ -5,7 +5,7 @@ class CoreFunctions:
print = Method( print = Method(
name="print", name="print",
bash_func="__ct_print", bash_func="__ct_print",
bash_impl='local msg="$1"; echo -e "$msg" >&3', bash_impl='echo -e "$1" >&3',
awk_builtin=lambda a: f"print {', '.join(a)}" if a else "print", awk_builtin=lambda a: f"print {', '.join(a)}" if a else "print",
min_args=1, max_args=1, min_args=1, max_args=1,
) )
......
...@@ -5,43 +5,43 @@ class FsMethods: ...@@ -5,43 +5,43 @@ class FsMethods:
read = Method( read = Method(
name="read", name="read",
bash_func="__ct_fs_read", bash_func="__ct_fs_read",
bash_impl='cat "$1"', bash_impl='__CT_RET=$(<"$1")',
min_args=1, max_args=1, min_args=1, max_args=1,
) )
write = Method( write = Method(
name="write", name="write",
bash_func="__ct_fs_write", bash_func="__ct_fs_write",
bash_impl='echo -n "$2" > "$1"', bash_impl='printf "%s" "$2" > "$1"; __CT_RET=""',
min_args=2, max_args=2, min_args=2, max_args=2,
) )
append = Method( append = Method(
name="append", name="append",
bash_func="__ct_fs_append", bash_func="__ct_fs_append",
bash_impl='echo -n "$2" >> "$1"', bash_impl='printf "%s" "$2" >> "$1"; __CT_RET=""',
min_args=2, max_args=2, min_args=2, max_args=2,
) )
exists = Method( exists = Method(
name="exists", name="exists",
bash_func="__ct_fs_exists", bash_func="__ct_fs_exists",
bash_impl='[[ -e "$1" ]] && echo "true" || echo "false"', bash_impl='[[ -e "$1" ]] && __CT_RET=true || __CT_RET=false',
min_args=1, max_args=1, min_args=1, max_args=1,
) )
remove = Method( remove = Method(
name="remove", name="remove",
bash_func="__ct_fs_remove", bash_func="__ct_fs_remove",
bash_impl='rm -f "$1"', bash_impl='rm -f "$1"; __CT_RET=""',
min_args=1, max_args=1, min_args=1, max_args=1,
) )
mkdir = Method( mkdir = Method(
name="mkdir", name="mkdir",
bash_func="__ct_fs_mkdir", bash_func="__ct_fs_mkdir",
bash_impl='mkdir -p "$1"', bash_impl='mkdir -p "$1"; __CT_RET=""',
min_args=1, max_args=1, min_args=1, max_args=1,
) )
list = Method( list = Method(
name="list", name="list",
bash_func="__ct_fs_list", bash_func="__ct_fs_list",
bash_impl='ls -1 "$1" 2>/dev/null || true', bash_impl='__CT_RET=$(ls -1 "$1" 2>/dev/null || true)',
min_args=1, max_args=1, min_args=1, max_args=1,
) )
open = Method( open = Method(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment