mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
Estimated hours taken: 16
Branches: main
Optimize calls to formatting functions and predicates such as
Str = string.format("%s_%d", [s(Prefix), i(Num)])
into
V1 = string.int_to_string(Num),
V2 = "_" ++ V1,
Str = Prefix ++ V2
essentially interpreting the format string at compile time rather than runtime.
At the moment, the optimization applies to calls to string.format/3,
io.format/3 and io.format/4, and only in the case where the format specifiers
are just "%c", "%d", or "%s", with no widths, precisions etc, though
that could be changed relatively easily, though at the cost of coupling
the compiler more tightly to the implementation of library/string.m.
(We don't handle %f, because float_to_string(F) yields a different string
than string.format("%f", [f(F)]). The former yields e.g. "1.23", while the
latter yields "1.23000".)
compiler/format_call.m:
Change the code that looks for malformed calls to formatting predicates
to also look for well-formed, optimizable calls, and to transform them
as shown above.
mdbcomp/prim_data.m:
List the standard library's string module here, since the compiler now
needs to know what its name is (it generates calls to its predicates).
compiler/options.m:
doc/user_guide.texi:
Add a new option, --optimize-format-calls, that calls for this
optimization. Make it enabled by default.
compiler/simplify.m:
Since the transformation in format_call.m will typically introduce
nested conjunctions, we now do it before the rest of the
simplifications. We also invoke the fixups needed by the output
of format_call.m if it actually optimized any code.
Delete the code that used to record whether the procedure has any
format calls, since it now comes too late.
Decide once, when setting up for processing a procedure, whether
we want to invoke format_call.m if appropriate, instead of doing it
later. This allows us to reduce the size of the simplications
data structure we pass around.
Protect the predicates that format_call.m can generate calls to
from being deleted by dead_pred_elim before the simplification pass.
compiler/det_analysis.m:
Since simplify.m itself cannot record early enough whether a given
procedure body contains calls to formatting predicates, do it here.
compiler/det_info.m:
Add a new field to the det_info that records whether we have seen
a call to a formatting predicate.
Add another field to the det_info that records the list of errors
we have found so far, so that we can stop passing it around separately.
compiler/hlds_pred.m:
Add a marker that allows det_analysis.m to record its conclusions.
compiler/hlds_goal.m:
Add utility predicate.
compiler/goal_util.m:
Make the predicates for creating new plain calls (and, for the sake of
uniformity, calls to foreign code) take instmap_deltas, such as those
created by the new utility functions in instmap.m. This allows these
predicates' caller to avoid creating unnecessary intermediate data
structures.
compiler/instmap.m:
Make an existing predicate into a function to allow it to be used
more flexibly.
Move some utility functions for creating instmap_deltas here from
table_gen.m, so that they can be used by other modules.
compiler/liveness.m:
Delete an unused predicate.
compiler/accumulator.m:
compiler/add_heap_ops.m:
compiler/add_trail_ops.m:
compiler/builtin_lib_types.m:
compiler/common.m:
compiler/complexity.m:
compiler/deep_profiling.m:
compiler/deforest.m:
compiler/dep_par_conj.m:
compiler/det_report.m:
compiler/distance_granularity.m:
compiler/granularity.m:
compiler/higher_order.m:
compiler/hlds_out.m:
compiler/inlining.m:
compiler/intermod.m:
compiler/modecheck_unify.m:
compiler/modes.m:
compiler/pd_util.m:
compiler/prog_type.m:
compiler/purity.m:
compiler/rbmm.region_transformation.m:
compiler/size_prof.m:
compiler/ssdebug.m:
compiler/table_gen.m:
compiler/try_expand.m:
compiler/typecheck.m:
compiler/unify_proc.m:
Conform to the changes above.
compiler/stm_expand.m:
Conform to the changes above. Also, build pairs by using "-" directly
as the function symbol, instead of this module's old practice
of doing it the slow way by calling the "pair" function.
tests/general/string_format_lib.m:
Cleanup the style of the code of this test case.
tests/hard_coded/opt_format.{m,exp}:
New test case to exercise the behavior of format_call.m's
transformation.
tests/hard_coded/Mmakefile:
tests/hard_coded/Mercury.options:
Enable the new test case.
278 lines
8.8 KiB
Mathematica
278 lines
8.8 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1999-2000, 2003-2007, 2009 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: bytecode_data.m.
|
|
% Authors: zs, aet, stayl.
|
|
%
|
|
% This module defines the representation of basic types used by the bytecode
|
|
% interpreter.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module backend_libs.bytecode_data.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% XXX This assumes strings contain 8-bit characters.
|
|
:- pred output_string(string::in, io::di, io::uo) is det.
|
|
:- pred string_to_byte_list(string::in, list(int)::out) is det.
|
|
|
|
:- pred output_byte(int::in, io::di, io::uo) is det.
|
|
|
|
% Spit out an `int' in a portable `highest common denominator' format.
|
|
% This format is: big-endian, 64-bit, 2's-complement int.
|
|
%
|
|
% NOTE: We -assume- the machine architecture uses 2's-complement.
|
|
%
|
|
:- pred output_int(int::in, io::di, io::uo) is det.
|
|
:- pred int_to_byte_list(int::in, list(int)::out) is det.
|
|
|
|
% Same as output_int and int_to_byte_list, except only use 32 bits.
|
|
%
|
|
:- pred output_int32(int::in, io::di, io::uo) is det.
|
|
:- pred int32_to_byte_list(int::in, list(int)::out) is det.
|
|
|
|
% Spit out a `short' in a portable format.
|
|
% This format is: big-endian, 16-bit, 2's-complement.
|
|
%
|
|
% NOTE: We -assume- the machine architecture uses 2's-complement.
|
|
%
|
|
:- pred output_short(int::in, io::di, io::uo) is det.
|
|
:- pred short_to_byte_list(int::in, list(int)::out) is det.
|
|
|
|
% Spit out a `float' in a portable `highest common denominator format.
|
|
% This format is: big-endian, 64-bit, IEEE-754 floating point value.
|
|
%
|
|
% NOTE: We -assume- the machine architecture uses IEEE-754.
|
|
%
|
|
:- pred output_float(float::in, io::di, io::uo) is det.
|
|
:- pred float_to_byte_list(float::in, list(int)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.compiler_util.
|
|
|
|
:- import_module char.
|
|
:- import_module int.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
output_string(Val, !IO) :-
|
|
string_to_byte_list(Val, List),
|
|
list.foldl(io.write_byte, List, !IO),
|
|
io.write_byte(0, !IO).
|
|
|
|
string_to_byte_list(Val, List) :-
|
|
% XXX This assumes strings contain 8-bit characters.
|
|
% Using char.to_int here is wrong; the output will depend on the Mercury
|
|
% implementation's representation of chars, so it may be different for
|
|
% different Mercury implementations. In particular, it will do the wrong
|
|
% thing for Mercury implementations which represent characters in Unicode.
|
|
string.to_char_list(Val, Chars),
|
|
ToInt = (pred(C::in, I::out) is det :- char.to_int(C, I)),
|
|
list.map(ToInt, Chars, List0),
|
|
list.append(List0, [0], List).
|
|
|
|
output_byte(Val, !IO) :-
|
|
( Val < 256 ->
|
|
io.write_byte(Val, !IO)
|
|
;
|
|
unexpected(this_file, "output_byte: byte does not fit in eight bits")
|
|
).
|
|
|
|
output_short(Val, !IO) :-
|
|
output_int(16, Val, !IO).
|
|
|
|
short_to_byte_list(Val, Bytes) :-
|
|
int_to_byte_list(16, Val, Bytes).
|
|
|
|
output_int32(IntVal, !IO) :-
|
|
output_int(32, IntVal, !IO).
|
|
|
|
int32_to_byte_list(IntVal, List) :-
|
|
int_to_byte_list(32, IntVal, List).
|
|
|
|
output_int(IntVal, !IO) :-
|
|
int.bits_per_int(IntBits),
|
|
( IntBits > bytecode_int_bits ->
|
|
unexpected(this_file, "output_int: " ++
|
|
"size of int is larger than size of bytecode integer.")
|
|
;
|
|
output_int(bytecode_int_bits, IntVal, !IO)
|
|
).
|
|
|
|
int_to_byte_list(IntVal, Bytes) :-
|
|
int.bits_per_int(IntBits),
|
|
( IntBits > bytecode_int_bits ->
|
|
unexpected(this_file, "int_to_byte_list: " ++
|
|
"size of int is larger than size of bytecode integer.")
|
|
;
|
|
int_to_byte_list(bytecode_int_bits, IntVal, Bytes)
|
|
).
|
|
|
|
:- pred output_int(int::in, int::in, io::di, io::uo) is det.
|
|
|
|
output_int(Bits, IntVal, !IO) :-
|
|
output_int(io.write_byte, Bits, IntVal, !IO).
|
|
|
|
:- pred int_to_byte_list(int::in, int::in, list(int)::out) is det.
|
|
|
|
int_to_byte_list(Bits, IntVal, Bytes) :-
|
|
output_int(list.cons, Bits, IntVal, [], RevBytes),
|
|
list.reverse(RevBytes, Bytes).
|
|
|
|
:- pred output_int(pred(int, T, T), int, int, T, T).
|
|
:- mode output_int(pred(in, in, out) is det, in, in, in, out) is det.
|
|
:- mode output_int(pred(in, di, uo) is det, in, in, di, uo) is det.
|
|
|
|
output_int(Writer, Bits, IntVal, !IO) :-
|
|
int.bits_per_int(IntBits),
|
|
(
|
|
Bits < IntBits,
|
|
int.pow(2, Bits - 1, MaxVal),
|
|
( IntVal >= MaxVal
|
|
; IntVal < -MaxVal
|
|
)
|
|
->
|
|
string.format(
|
|
"error: bytecode_data.output_int: %d does not fit in %d bits",
|
|
[i(IntVal), i(Bits)], Msg),
|
|
unexpected(this_file, Msg)
|
|
;
|
|
true
|
|
),
|
|
( Bits > IntBits ->
|
|
ZeroPadBytes = (Bits - IntBits) // bits_per_byte
|
|
;
|
|
ZeroPadBytes = 0
|
|
),
|
|
output_padding_zeros(Writer, ZeroPadBytes, !IO),
|
|
BytesToDump = Bits // bits_per_byte,
|
|
FirstByteToDump = BytesToDump - ZeroPadBytes - 1,
|
|
output_int_bytes(Writer, FirstByteToDump, IntVal, !IO).
|
|
|
|
:- func bytecode_int_bits = int.
|
|
|
|
bytecode_int_bits = bits_per_byte * bytecode_int_bytes.
|
|
|
|
:- func bytecode_int_bytes = int.
|
|
|
|
bytecode_int_bytes = 8.
|
|
|
|
:- func bits_per_byte = int.
|
|
|
|
bits_per_byte = 8.
|
|
|
|
:- pred output_padding_zeros(pred(int, T, T), int, T, T).
|
|
:- mode output_padding_zeros(pred(in, in, out) is det, in, in, out) is det.
|
|
:- mode output_padding_zeros(pred(in, di, uo) is det, in, di, uo) is det.
|
|
|
|
output_padding_zeros(Writer, NumBytes, !IO) :-
|
|
( NumBytes > 0 ->
|
|
call(Writer, 0, !IO),
|
|
NumBytes1 = NumBytes - 1,
|
|
output_padding_zeros(Writer, NumBytes1, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred output_int_bytes(pred(int, T, T), int, int, T, T).
|
|
:- mode output_int_bytes(pred(in, in, out) is det, in, in, in, out) is det.
|
|
:- mode output_int_bytes(pred(in, di, uo) is det, in, in, di, uo) is det.
|
|
|
|
output_int_bytes(Writer, ByteNum, IntVal, !IO) :-
|
|
( ByteNum >= 0 ->
|
|
BitShifts = ByteNum * bits_per_byte,
|
|
Byte = (IntVal >> BitShifts) mod (1 << bits_per_byte),
|
|
ByteNum1 = ByteNum - 1,
|
|
call(Writer, Byte, !IO),
|
|
output_int_bytes(Writer, ByteNum1, IntVal, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
output_float(Val, !IO) :-
|
|
float_to_float64_bytes(Val, B0, B1, B2, B3, B4, B5, B6, B7),
|
|
output_byte(B0, !IO),
|
|
output_byte(B1, !IO),
|
|
output_byte(B2, !IO),
|
|
output_byte(B3, !IO),
|
|
output_byte(B4, !IO),
|
|
output_byte(B5, !IO),
|
|
output_byte(B6, !IO),
|
|
output_byte(B7, !IO).
|
|
|
|
float_to_byte_list(Val, [B0, B1, B2, B3, B4, B5, B6, B7]) :-
|
|
float_to_float64_bytes(Val, B0, B1, B2, B3, B4, B5, B6, B7).
|
|
|
|
% Convert a `float' to the representation used in the bytecode.
|
|
% That is, a sequence of eight bytes.
|
|
%
|
|
:- pred float_to_float64_bytes(float::in,
|
|
int::out, int::out, int::out, int::out,
|
|
int::out, int::out, int::out, int::out) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
float_to_float64_bytes(FloatVal::in, B0::out, B1::out, B2::out,
|
|
B3::out, B4::out, B5::out, B6::out, B7::out),
|
|
[promise_pure, will_not_call_mercury],
|
|
"
|
|
{
|
|
MR_Float64 float64;
|
|
unsigned char *raw_mem_p;
|
|
|
|
float64 = (MR_Float64) FloatVal;
|
|
raw_mem_p = (unsigned char *) &float64;
|
|
|
|
#if defined(MR_BIG_ENDIAN)
|
|
B0 = raw_mem_p[0];
|
|
B1 = raw_mem_p[1];
|
|
B2 = raw_mem_p[2];
|
|
B3 = raw_mem_p[3];
|
|
B4 = raw_mem_p[4];
|
|
B5 = raw_mem_p[5];
|
|
B6 = raw_mem_p[6];
|
|
B7 = raw_mem_p[7];
|
|
#elif defined(MR_LITTLE_ENDIAN)
|
|
B7 = raw_mem_p[0];
|
|
B6 = raw_mem_p[1];
|
|
B5 = raw_mem_p[2];
|
|
B4 = raw_mem_p[3];
|
|
B3 = raw_mem_p[4];
|
|
B2 = raw_mem_p[5];
|
|
B1 = raw_mem_p[6];
|
|
B0 = raw_mem_p[7];
|
|
#else
|
|
#error ""Weird-endian architecture""
|
|
#endif
|
|
}
|
|
").
|
|
|
|
float_to_float64_bytes(_FloatVal, _B0, _B1, _B2, _B3, _B4, _B5, _B6, _B7) :-
|
|
sorry(this_file, "float_to_float64_bytes for non-C target").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "bytecode_data.m".
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module bytecode_data.
|
|
%---------------------------------------------------------------------------%
|
|
|