Age | Commit message (Collapse) | Author |
|
|
|
All keys in an orddict must be unique. sys_core_fold:sub_sub_scope/1
broke that rule. It was probably harmless, but it is better to
avoid such rule violations.
|
|
As part of sys_core_fold, variables involved in bit syntax
matching would be annotated when it would be safe for a later
pass to do the delayed sub-binary creation optimization.
An implicit assumption regarding the annotation was that the
code must not be further optimized. That assumption was broken
in 05130e48555891, which introduced a fixpoint iteration
(applying the optimizations until there were no more changes).
That means that a variable could be annotated as safe for
reusing the match context in one iteration, but a later iteration
could rewrite the code in a way that would make the optimization
unsafe.
One way to fix this would be to clear all reuse_for_context
annotations before each iteration. But that would be wasteful.
Instead I chose to fix the problem by moving out the annotation
code to a separate pass (sys_core_bsm) that is run later after
all major optimizations of Core Erlang has been done.
|
|
Code with huge literal case expressions such as the following would
compile very slowly:
case "Very long literal string (thousands of characters)..." of
.
.
.
end.
The reason is that in the case optimization each character in the
string would be handled individually. Fix this bug by handling
literals all at once.
|
|
The compiler produces poor code for complex guard expressions with andalso/orelse.
Here is an example from the filename module:
-define(IS_DRIVELETTER(Letter),(((Letter >= $A) andalso (Letter =< $Z)) orelse
((Letter >= $a) andalso (Letter =< $z)))).
skip_prefix(Name, false) ->
Name;
skip_prefix([L, DrvSep|Name], DrvSep) when ?IS_DRIVELETTER(L) ->
Name;
skip_prefix(Name, _) ->
Name.
beam_bool fails to simplify the code for the guard, leaving several 'bif'
instructions:
{function, skip_prefix, 2, 49}.
{label,48}.
{line,[{location,"filename.erl",187}]}.
{func_info,{atom,filename},{atom,skip_prefix},2}.
{label,49}.
{test,is_ne_exact,{f,52},[{x,1},{atom,false}]}.
{test,is_nonempty_list,{f,52},[{x,0}]}.
{get_list,{x,0},{x,2},{x,3}}.
{test,is_nonempty_list,{f,52},[{x,3}]}.
{get_list,{x,3},{x,4},{x,5}}.
{bif,'=:=',{f,52},[{x,1},{x,4}],{x,6}}.
{test,is_ge,{f,50},[{x,2},{integer,65}]}.
{bif,'=<',{f,52},[{x,2},{integer,90}],{x,7}}.
{test,is_eq_exact,{f,51},[{x,7},{atom,false}]}.
{test,is_ge,{f,50},[{x,2},{integer,97}]}.
{bif,'=<',{f,52},[{x,2},{integer,122}],{x,7}}.
{jump,{f,51}}.
{label,50}.
{move,{atom,false},{x,7}}.
{label,51}.
{bif,'=:=',{f,52},[{x,7},{atom,true}],{x,7}}.
{test,is_eq_exact,{f,52},[{x,6},{atom,true}]}.
{test,is_eq_exact,{f,52},[{x,7},{atom,true}]}.
{move,{x,5},{x,0}}.
return.
{label,52}.
return.
We can add optimizations of guard tests to v3_kernel to achive a better result:
{function, skip_prefix, 2, 49}.
{label,48}.
{line,[{location,"filename.erl",187}]}.
{func_info,{atom,filename},{atom,skip_prefix},2}.
{label,49}.
{test,is_ne_exact,{f,51},[{x,1},{atom,false}]}.
{test,is_nonempty_list,{f,51},[{x,0}]}.
{get_list,{x,0},{x,2},{x,3}}.
{test,is_nonempty_list,{f,51},[{x,3}]}.
{get_list,{x,3},{x,4},{x,5}}.
{test,is_eq_exact,{f,51},[{x,1},{x,4}]}.
{test,is_ge,{f,51},[{x,2},{integer,65}]}.
{test,is_lt,{f,50},[{integer,90},{x,2}]}.
{test,is_ge,{f,51},[{x,2},{integer,97}]}.
{test,is_ge,{f,51},[{integer,122},{x,2}]}.
{label,50}.
{move,{x,5},{x,0}}.
return.
{label,51}.
return.
Looking at the STDLIB application, there were 112 lines of BIF calls in guards
that beam_bool failed to convert to test instructions. This commit eliminates
all those BIF calls.
Here is how I counted the instructions:
$ PATH=$ERL_TOP/bin:$PATH erlc -I ../include -I ../../kernel/include -S *.erl
$ grep "bif,'[=<>]" *.S | grep -v f,0
dets.S: {bif,'=:=',{f,547},[{x,4},{atom,read_write}],{x,4}}.
dets.S: {bif,'=:=',{f,547},[{x,5},{atom,saved}],{x,5}}.
dets.S: {bif,'=:=',{f,589},[{x,5},{atom,read}],{x,5}}.
.
.
.
$ grep "bif,'[=<>]" *.S | grep -v f,0 | wc
112 224 6765
$
|
|
The fixpoint iteration added in 05130e48 makes those calls
superfluous.
|
|
There are two calls opt_not_in_let(). Since 05130e4855
introduced iteration to a fixpoint, only the first call
is needed. Removing the redundant call will slightly speed
up compilation.
|
|
|
|
* maint:
Don't copy funs into guards
|
|
Funs must not be created in guards. The instruction for creating
a fun clobbers all X registers, which is a bad thing to do in
a guard.
|
|
* maint:
Don't let inline_list_funcs degrade optimizations
|
|
83199af0263 refactored sys_core_fold to break out the code for the
inline_lists_funcs option to its own module. Unfortunately, it also
accidentally turned off compile-time evaluation of calls to BIFs with
wholly or partial constant arguments.
For example, the code for the following funtion gets much worse
when inline_list_funcs is used:
b() ->
R0 = #r{},
R1 = setelement(1+2, R0, "deux"),
R2 = setelement(1+3, R1, "trois"),
R3 = setelement(1+5, R2, "cinq"),
R4 = setelement(1+2, R3, "DEUX"),
R4.
ERL-285
|
|
* josevalim/compiler/at-var/PR-1081/OTP-13924:
Use @ in variable names generated by core and kernel
|
|
Run the optimizations until a fixpoint is reached, or until
the maximum iteration count is reached.
The hope is that in the future we can many small optimizations
instead of optimizations that try to do everything in one pass.
This change allows us to remove the ad-hoc calls to expr/2
to run more optimizations on a piece of code.
|
|
The optimization that avoids building a tuple in a case
expression would not work if any clause matched a tuple
as in the following example:
f(A, B) ->
case {A,B} of
{<<X>>,Y} ->
{X,Y}
end.
The generated Core Erlang code would look like this (note
the tuples in the case expression and the pattern):
'f'/2 =
fun (_cor1,_cor0) ->
case {_cor1,_cor0} of
<{#{#<X>(8,1,'integer',['unsigned'|['big']])}#,Y}>
when 'true' ->
{X,Y}
.
.
.
end
It is expected that the code should look like this (note
that tuples have been replaced with "values"):
'f'/2 =
fun (_cor1,_cor0) ->
%% Line 5
case <_cor1,_cor0> of
<#{#<X>(8,1,'integer',['unsigned'|['big']])}#,Y> ->
{X,Y}
.
.
.
end
While at it, also fix bugs in the handling of pattern with
aliases. The bindings were produced in the wrong order (creating
'let's with referring to free variables), but in most cases
the incorrect bindings were discarded later without causing any
harm.
|
|
703e8f4490bf broke the scope verification code (by calling
ordsets:is_subset/2 with an unsorted second argument).
While we are it, also optimize the verification function
by avoiding converting the map to a sorted list.
|
|
The previous variable names can be generated by
projects like LFE and Elixir, leading to possible
conflicts. Our first to choice to solve such conflicts
was to use $ but that's not a valid variable name in core.
Therefore we picked @ which is currently supported and
still reduces the chance of conflicts.
|
|
* maint:
[snmp] Correct bug when path to mib contains UTF-8 characters
[ic] Fix but when UTF-8 character in path to idl spec
sys_core_fold: Don't move a fun into a guard
|
|
Moving a fun into a guard may cause code that is not accepted
by beam_validator.
|
|
Implement as ceil/1 and floor/1 as new guard BIFs (essentially part of
Erlang language). They are guard BIFs because trunc/1 is a guard
BIF. It would be strange to have trunc/1 as a part of the language, but
not ceil/1 and floor/1.
|
|
Add more filename/line number annotations while translating to
Core Erlang in v3_core, and ensure that sys_core_fold retains
existing annotations. The goal is to avoid that sys_core_fold
generate warnings with "no_file" instead of a filename.
|
|
Rewrite code such as:
X = not_a_fun,
X()
to:
error({badfun,not_a_fun})
Also generate a warning.
|
|
|
|
Binary matching can be confusing. For example:
1> <<-1>> = <<-1>>.
** exception error: no match of right hand side value <<"ÿ">>
2>
When constructing binaries, the value will be masked to fit in
the binary segment. But no such masking happens when matching
binaries.
One solution that we considered was to do the same masking when
matching. We have rejected that solution for several reasons:
* Masking in construction is highly controversial and by some
people considered a bad design decision.
* While masking of unsigned numbers can be understood, masking of
signed numbers it not easy to understand.
* Then there is the question of backward compatibility. Adding
masking to matching would mean that clauses that did not match
earlier would start to match. That means that code that has
never been tested will be executed. Code that has not been
tested will usually not work.
Therefore, we have decided to warn for binary patterns that cannot
possibly match.
While we are it, we will also warn for the following example where
size for a binary segment is invalid:
bad_size(Bin) ->
BadSize = bad_size,
<<42:BadSize>> = Bin.
That example would crash the HiPE compiler because the BEAM compiler
would generate a bs_get_integer2 instruction with an invalid size
field. We can avoid that crash if sys_core_fold not only warns for bad
binary pattern, but also removes the clauses that will not match.
Reported-by: http://bugs.erlang.org/browse/ERL-44
Reported-by: Kostis Sagonas
|
|
As a preparation for checking binary patterns we will add
var_list/2 that will work as pattern_list/2 but is guaranteed
not to throw an exception. That way, we will only have to use
try...catch for the few remaining calls to pattern_list/2.
|
|
Save work the *extremely* common case that the guard is
a literal.
|
|
* maint:
Fix missing filename and line number in warning
Conflicts:
lib/compiler/test/bs_match_SUITE.erl
|
|
When the 'bin_opt_info' is given, warnings without filenames
and line numbers could sometimes be produced:
no_file: Warning: INFO: matching non-variables after
a previous clause matching a variable will prevent delayed
sub binary optimization
The reason for the missing information is that #c_alias{} records lack
location information. There are several ways to fix the problem. The
easiest seems to be to get the location information from the
code).
Noticed-by: José Valim
|
|
Knowing that a BIF returns an integer makes it possible to
replace '==' with the cheaper '=:=' test.
|
|
|
|
* bjorn/compiler/spurious-warning:
sys_core_fold: Eliminate warnings for unused terms in effect context
sys_core_fold: Eliminate warnings for unused terms
|
|
The optimization introduced in 0a0d39d351fc could cause spurious
warnings of the type: "a term is constructed, but never used".
That would happen for constructs in effect context.
To avoid those warnings, we will need to apply warning suppression
also in effect context.
|
|
Using Maps as type information container speedups files like cow_http_hd.erl
by ~500ms. Previously spent ~60% of the time in orddict:store/3.
|
|
|
|
|
|
The optimization introduced in 0a0d39d351fc would cause spurious
warnings of the type: "a term is constructed, but never used".
To avoid the warning, we must mark not only tuples and lists as
compiler_generated, but also each element. We must also propagate
compiler_generated annotations in lets. For example, if we have:
let <X -| ['compiler_generated']> = 42 in X + 1
we must propagate the compiler_generated annotation to the literal
when do constant propagation:
42 -| ['compiler_generated'] + 1
|
|
86fbd6d76d strengthened type optimization in lets. As a result of
the stronger optimizations, special care had to be taken to
suppress false warnings.
It turns out that false warnings can still slip through. Slapping
on a 'compiler_generated' annotation at the top-level of a
complex term such as #c_tuple{} may not suppress all warnings.
We will need to go deeper into the term to eliminate all warnings.
|
|
According to EEP-43 for maps, a 'badmap' exception should be
generated when an attempt is made to update non-map term such as:
<<>>#{a=>42}
That was not implemented in the OTP 17.
José Valim suggested that we should take the opportunity to
improve the errors coming from map operations:
http://erlang.org/pipermail/erlang-questions/2015-February/083588.html
This commit implement better errors from map operations similar
to his suggestion.
When a map update operation (Map#{...}) or a BIF that expects a map
is given a non-map term, the exception will be:
{badmap,Term}
This kind of exception is similar to the {badfun,Term} exception
from operations that expect a fun.
When a map operation requires a key that is not present in a map,
the following exception will be raised:
{badkey,Key}
José Valim suggested that the exception should be
{badkey,Key,Map}. We decided not to do that because the map
could potentially be huge and cause problems if the error
propagated through links to other processes.
For BIFs, it could be argued that the exceptions could be simply
'badmap' and 'badkey', because the bad map and bad key can be found in
the argument list for the BIF in the stack backtrace. However, for the
map update operation (Map#{...}), the bad map or bad key will not be
included in the stack backtrace, so that information must be included
in the exception reason itself. For consistency, the BIFs should raise
the same exceptions as update operation.
If more than one key is missing, it is undefined which of
keys that will be reported in the {badkey,Key} exception.
|
|
For a long time, there has been an optimization for:
{V1,V2,...} = case Expr of Pat -> ... {Val1,Val2,...}; ... end
that avoids building the tuples. The construct looks like this
in Core Erlang:
let <V> = case X of
Pattern -> {Y,Z}
end
in case V of
{A,B} -> A+B
end
The current optimization will try to replace the second 'case'
with a 'let':
let <A,B> = case X of
Pattern -> <Y,Z>
end
in A+B
Simple variations of the construct would prevent the optimizations;
for example this one:
let <V> = case X of
Pattern -> {'ok',Val}
end
in case V of
{ok,Val} -> Val
end
The problem is that the optimization tries to do too much. By
making the optimization do less and have it depend on other
optimizations to finish the job, it will become more powerful.
Thus we can rewrite the code like this:
let <V1,V2> = case X of
Pattern -> <'ok',Val>
end
in let <V> = {V1,V2}
in case V of
{ok,Val} -> Val
end
Note that the second case is unchanged. The other optimizations in the
sys_core_fold module will optimize the second 'case' and eliminate the
building of the tuple.
|
|
Optimize away 'not' in sys_core_fold instead of in beam_block
and beam_dead, as we can do a better job in sys_core_fold.
I modified the test suite temporarily to never turn off Core Erlang
modifications and looked at the coverage. With the new optimizations
active in sys_core_fold, the code in beam_block and beam_dead did not
find a single 'not' that it could optimize. That proves that the new
optimization is at least as good as the old one. Manually, I could
also verify that the new optimization would optimize some variations
of 'not' that the old one would not handle.
|
|
More aggressive optimizations that we plan to introduce could cause
spurious compiler warnings.
|
|
The 'try' ... 'catch' is problematic. Firstly, if no optimization
is possible, an exception will always be thrown. Secondly, bugs
in the code will go unnoticed.
|
|
'=:=' is a cheaper operation than '==', so we should always
use '=:=' if the result will be the same as if '==' were used.
|
|
|
|
Make sure that we take extract all possible type information when
optimizing a 'let' construct.
Since the stronger optimization may generate false warnings, we also
need to take special care to suppress false warnings.
|
|
If we have a sequence of put_map_* instructions operating on the
same map, it will be more efficient if we can have one is_map/2
instruction before put_map_* instructions, so that each put_map_*
does not need to test whether the argument is a map.
|
|
649d6e73 simplified opt_simple_let_2/6 a little bit too much,
so that some list comprehensions in effect context were not
properly tail-recursive.
|
|
In cd1eaf0116190, opt_simple_let_2/6 was updated to do the same
optimizations in 'value' and 'effect' context.
Coalesce the clauses for 'value' and 'effect' context to one to
make it clear that they do the same thing.
|
|
The code for inlining high-order functions from the lists module
is quite annoying when you try to navigate the sys_core_fold
module. Break out the code into its own module.
|
|
Those functions allow us to clean up some more code.
|