Arg.Language
A high-level ABI-specification language.
This module makes it easier to describe various calling conventions using high-level combinators, built on top of the lower level primitives of the Arg
language.
The idea is that you can open Arg.Language
and have all combinators available in the scope. To enable language extension, we also provide the versioned modules, Arg.Language.V1
, and so on. Every extension of the language, i.e., an addition of a new operator or combinator, will go into a separate module, so that if you are using Arg.Language.Vx
it is guaranteed that the language changes will not break anything.
In Arg.Language
we describe calling conventions declaratively using a list of commands, where each command is a guarded statement, i.e., a pair of a predicate and the statement. The statement is using the Arg
operators to describe argument passing routine, and the predicate checks if this is applicable to the given arguement. Various combinators combine commands and predicates, into a final statement that fully describes the argument passing procedure for the given subroutine.
type predicate = ctype -> bool
type predicates = predicate list
type statements = statement list
type commands = command list
module type V1 = sig ... end
include V1
val install :
Bap_core_theory.Theory.Target.t ->
Bap_c_size.base ->
((?finish:unit t ->
return:(alignment:int -> int -> statement) ->
(alignment:int -> int -> statement) ->
unit t) ->
unit t) ->
unit
install target data_model specification
The toplevel function that is used to install the calling convention for the given target. The general syntax follows this structure,
describe t data @@ fun declare ->
let* arena1 = <arena-specification> in
let* arenaN = <arena-specification> in
let return ~alignment x = <statement> in
let arg ~alignment x = <statement> in
let finish = <finalization-procedure> in
let* () = <initialization-procedure> in
declare ~finish ~return arg
For example, the following specification describes ARM calling convention AAPCS32,
let define t =
install t model @@ fun describe ->
let* iargs = Arg.Arena.iargs t in
let* irets = Arg.Arena.irets t in
let rev = Theory.Endianness.(Theory.Target.endianness t = le) in
let return ~alignment:_ size = select [
C.Type.is_basic, select [
is (size <= 32 * 2), Arg.registers ~rev irets;
otherwise, Arg.reference iargs;
];
is (size <= 32), Arg.register irets;
otherwise, Arg.reference iargs;
] in
describe ~return @@ fun ~alignment _ ->
sequence [
is (alignment = 64), const (Arg.align_even iargs);
always, choose [
Arg.split_with_memory ~rev iargs;
Arg.memory
];
]
sequence cmd
executes a sequence of predicated statements.
Executes all commands in the specified order. A command's statement is executed if the commands predicate is evaluates to true
. Otherwise the statement is skipped.
If an evaluated statement rejects a computation then the whole sequence will be rejected.
select t spec
selects the first applicable option to pass t
.
The form,
select [
pred1, option1;
pred2, option2;
...,...;
predN, optionN;
]
Tries pred1 arg
, pred2 arg
, ..., predN arg
in order until the first one that returns true
and uses the corresponding option to pass the argument.
The computation is rejected if either the selected option rejects the computation or none options were selected.
case classifer commands
case analysis.
The case combinator first classfies the argument using the classfier
function, which shall return a polymoprhic variant, and then selects a command that matches the selected class.
Rejects the computation if there's no matching class.
val any : predicates -> predicate
any ps
holds if any of ps
holds.
val all : predicates -> predicate
all ps
holds if all of ps
hold.
val neither : predicates -> predicate
neither ps
holds if neither of ps
hold.
val is : bool -> predicate
is cnd
holds if cnd
holds.
Example,
[
is (size > 64), memory;
otherwise, registers;
]
val otherwise : predicate
otherwise
is a predicate that is always true
.
I.e., it is is true
.
This predicate is supposed to be used with the select
combinator as the last, catch-all, predicate, e.g.,
select arg [
is_fundamental, pass_fundamental;
is_floating, pass_floating;
otherwise, pass_memory;
]
val always : predicate
always
is a predicate that is always true
, i.e., is true
.
val never : predicate
never
is a predicate that never holds, i.e., is false
.
val choose : statements -> statement
choose options
tries options in order until the first on that is not rejected.
This combinator is like choice
but is supposed to be used inside select
, e.g.,
select arg [
p1, choose [o1, o2];
either [p2; p3], choose [o3,o4];
]
val combine : statements -> statement
combine xs
combines statements into a signle statement.
Evaluates statements in the order they are specified. If any of the statements rejects then the whole statement will be rejected.
include Monads.Std.Monad.Syntax.S with type 'a t := 'a t
val (!!) : 'a -> 'a t
!!x
is return x
!$$$$f
is Lift.quaternary f
include Monads.Std.Monad.Syntax.Let.S with type 'a t := 'a t