Functional syntax (Was: RE: [mercury-users] Hi, and determini sm)

Fergus Henderson fjh at cs.mu.OZ.AU
Wed Feb 7 15:57:17 AEDT 2001


On 07-Feb-2001, Richard A. O'Keefe <ok at atlas.otago.ac.nz> wrote:
> Has anyone an example where packing below the byte level would save
> useful amounts of memory, in Mercury?

Here's an example from the Mercury compiler.
In the new back-end, we compile Mercury programs down to
an abstract representation that includes support for
languages like Java.  Each declaration has a set of
declaration flags associated with it.

We use an abstract type

	:- type decl_flags.

Conceptually, this type should be equivalent to

	:- type decl_flags ---> decl_flags(
		access :: access,		% public/private/protected
		per_instance :: per_instance,	% one_copy/per_instance
		virtuality :: virtuality,	% virtual/non_virtual
		finality :: finality,		% final/overridable (funcs only)
		constness :: constness,		% const/modifiable  (data only)
		abstractness :: abstractness	% abstract/concrete
	).

where the revelant field types are defined as

	:- type access
		--->	public
		;	protected
		;	private
		;	default.	% Java "default" access: accessible to
					% anything defined in the same package.

	:- type per_instance
		--->	one_copy	% i.e. "static" storage duration
					% (but not necessarily static linkage)
					% or static member function
		;	per_instance.	% i.e. "auto" local variable in
					% function, or non-static member of
					% class.

	:- type virtuality
		--->	non_virtual
		;	virtual.

	:- type finality
		--->	overridable
		;	final.

	:- type constness
		--->	modifiable
		;	const.

	:- type abstractness
		--->	concrete
		;	abstract.

But we make the type abstract,

	:- interface.

	:- type decl_flags.

	:- func access(decl_flags) = access.
	:- func per_instance(decl_flags) = per_instance.
	:- func virtuality(decl_flags) = virtuality.
	:- func finality(decl_flags) = finality.
	:- func constness(decl_flags) = constness.
	:- func abstractness(decl_flags) = abstractness.

	:- func set_access(decl_flags, access) = decl_flags.
	:- func set_per_instance(decl_flags, per_instance) = decl_flags.
	:- func set_virtuality(decl_flags, virtuality) = decl_flags.
	:- func set_finality(decl_flags, finality) = decl_flags.
	:- func set_constness(decl_flags, constness) = decl_flags.
	:- func set_abstractness(decl_flags, abstractness) = decl_flags.

	:- func init_decl_flags(access, per_instance, virtuality, finality,
			constness, abstractness) = decl_flags.

and implement it using bit twiddling:

	:- implementation.

	%
	% We represent the set of declaration flags as a bunch of bit-fields
	% packed into a single int.
	%
	:- type decl_flags == int.

	%
	% Here we define which bits are used to store each bitfield.
	%

	:- func access_bits(access) = int.
	:- mode access_bits(in) = out is det.
	:- mode access_bits(out) = in is semidet.
	access_bits(public)  	= 0x00.
	access_bits(private) 	= 0x01.
	access_bits(protected)	= 0x02.
	access_bits(default)	= 0x03.
	% 0x4 - 0x7 reserved

	:- func access_mask = int.
	access_mask = 0x07.

	:- func per_instance_bits(per_instance) = int.
	:- mode per_instance_bits(in) = out is det.
	:- mode per_instance_bits(out) = in is semidet.
	per_instance_bits(one_copy) 	= 0x00.
	per_instance_bits(per_instance)	= 0x08.

	:- func per_instance_mask = int.
	per_instance_mask = per_instance_bits(per_instance).

	:- func virtuality_bits(virtuality) = int.
	:- mode virtuality_bits(in) = out is det.
	:- mode virtuality_bits(out) = in is semidet.
	virtuality_bits(non_virtual) 	= 0x00.
	virtuality_bits(virtual)	= 0x10.

	:- func virtuality_mask = int.
	virtuality_mask = virtuality_bits(virtual).

	:- func finality_bits(finality) = int.
	:- mode finality_bits(in) = out is det.
	:- mode finality_bits(out) = in is semidet.
	finality_bits(overridable) 	= 0x00.
	finality_bits(final)		= 0x20.

	:- func finality_mask = int.
	finality_mask = finality_bits(final).

	:- func constness_bits(constness) = int.
	:- mode constness_bits(in) = out is det.
	:- mode constness_bits(out) = in is semidet.
	constness_bits(modifiable) 	= 0x00.
	constness_bits(const)		= 0x40.

	:- func constness_mask = int.
	constness_mask = constness_bits(const).

	:- func abstractness_bits(abstractness) = int.
	:- mode abstractness_bits(in) = out is det.
	:- mode abstractness_bits(out) = in is semidet.
	abstractness_bits(abstract) 	= 0x00.
	abstractness_bits(concrete)	= 0x80.

	:- func abstractness_mask = int.
	abstractness_mask = abstractness_bits(concrete).

	%
	% Here we define the functions to lookup a member of the set.
	%

	access(Flags) = promise_det(pred(Access::out) is semidet :-
		Flags /\ access_mask = access_bits(Access)).

	per_instance(Flags) = promise_det(pred(PerInstance::out) is semidet :-
		Flags /\ per_instance_mask = per_instance_bits(PerInstance)).

	virtuality(Flags) = promise_det(pred(Virtuality::out) is semidet :-
		Flags /\ virtuality_mask = virtuality_bits(Virtuality)).

	finality(Flags) = promise_det(pred(Finality::out) is semidet :-
		Flags /\ finality_mask = finality_bits(Finality)).

	constness(Flags) = promise_det(pred(Constness::out) is semidet :-
		Flags /\ constness_mask = constness_bits(Constness)).

	abstractness(Flags) = promise_det(pred(Abstractness::out) is semidet :-
		Flags /\ abstractness_mask = abstractness_bits(Abstractness)).

	:- func promise_det(pred(T)) = T.
	:- mode promise_det(pred(out) is semidet) = out is det.

	promise_det(Pred) = X :-
		(if Pred(X0) then X = X0 else error("promise_det failed")).


	%
	% Here we define the functions to set a member of the set.
	%

	set_access(Flags, Access) =
		Flags /\ \access_mask \/ access_bits(Access).

	set_per_instance(Flags, PerInstance) =
		Flags /\ \per_instance_mask \/ per_instance_bits(PerInstance).

	set_virtuality(Flags, Virtuality) =
		Flags /\ \virtuality_mask \/ virtuality_bits(Virtuality).

	set_finality(Flags, Finality) =
		Flags /\ \finality_mask \/ finality_bits(Finality).

	set_constness(Flags, Constness) =
		Flags /\ \constness_mask \/ constness_bits(Constness).

	set_abstractness(Flags, Abstractness) =
		Flags /\ \abstractness_mask \/ abstractness_bits(Abstractness).

	init_decl_flags(Access, PerInstance, Virtuality, Finality, Constness,
			Abstractness) =
		access_bits(Access) \/
		per_instance_bits(PerInstance) \/
		virtuality_bits(Virtuality) \/
		finality_bits(Finality) \/
		constness_bits(Constness) \/
		abstractness_bits(Abstractness).

I must confess that I didn't actually bother to benchmark things to
see what effect this had on performance.  But I think it is likely
to be a win, due to the reduced number of memory allocations;
for the Mercury compiler itself, GC cost is a significant proportion
of the compiler's running time.

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
                                    |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-users mailing list
post:  mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the users mailing list