Skip to content

Commit 29c138e

Browse files
authored
Handle rip-relative addressing (#2029)
The idea to parse `rip` specially rather than as a register is due to Andres.
1 parent 37929dc commit 29c138e

File tree

3 files changed

+83
-29
lines changed

3 files changed

+83
-29
lines changed

src/Assembly/Equality.v

+16-1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,20 @@ Proof. rewrite <- AccessSize_beq_eq; destruct (x =? y)%AccessSize; intuition con
102102
Global Instance AccessSize_beq_compat : Proper (eq ==> eq ==> eq) AccessSize_beq | 10.
103103
Proof. repeat intro; subst; reflexivity. Qed.
104104

105+
Declare Scope rip_relative_kind_scope.
106+
Delimit Scope rip_relative_kind_scope with rip_relative_kind.
107+
Bind Scope rip_relative_kind_scope with rip_relative_kind.
108+
109+
Infix "=?" := rip_relative_kind_beq : rip_relative_kind_scope.
110+
111+
Global Instance rip_relative_kind_beq_spec : reflect_rel (@eq rip_relative_kind) rip_relative_kind_beq | 10
112+
:= reflect_of_beq internal_rip_relative_kind_dec_bl internal_rip_relative_kind_dec_lb.
113+
Definition rip_relative_kind_beq_eq x y : (x =? y)%rip_relative_kind = true <-> x = y := conj (@internal_rip_relative_kind_dec_bl _ _) (@internal_rip_relative_kind_dec_lb _ _).
114+
Lemma rip_relative_kind_beq_neq x y : (x =? y)%rip_relative_kind = false <-> x <> y.
115+
Proof. rewrite <- rip_relative_kind_beq_eq; destruct (x =? y)%rip_relative_kind; intuition congruence. Qed.
116+
Global Instance rip_relative_kind_beq_compat : Proper (eq ==> eq ==> eq) rip_relative_kind_beq | 10.
117+
Proof. repeat intro; subst; reflexivity. Qed.
118+
105119
Declare Scope MEM_scope.
106120
Delimit Scope MEM_scope with MEM.
107121
Bind Scope MEM_scope with MEM.
@@ -111,7 +125,8 @@ Definition MEM_beq (x y : MEM) : bool
111125
&& option_beq String.eqb x.(mem_base_label) y.(mem_base_label)
112126
&& (option_beq REG_beq x.(mem_base_reg) y.(mem_base_reg))
113127
&& (option_beq (prod_beq _ _ Z.eqb REG_beq) x.(mem_scale_reg) y.(mem_scale_reg))
114-
&& (option_beq Z.eqb x.(mem_offset) y.(mem_offset)))%bool.
128+
&& (option_beq Z.eqb x.(mem_offset) y.(mem_offset))
129+
&& (rip_relative_kind_beq x.(rip_relative) y.(rip_relative)))%bool.
115130
Global Arguments MEM_beq !_ !_ / .
116131

117132
Infix "=?" := MEM_beq : MEM_scope.

src/Assembly/Parse.v

+54-26
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,16 @@ Definition parse_label : ParserAction string
8080
(fun '(char, ls) => string_of_list_ascii (char :: ls))
8181
(([a-zA-Z] || parse_any_ascii "._?$") ;;
8282
(([a-zA-Z] || parse_any_ascii "0123456789_$#@~.?")* )).
83+
8384
Definition parse_non_access_size_label : ParserAction string
84-
:= parse_lookahead_not parse_AccessSize ;;R parse_label.
85+
:= parse_lookahead_not parse_AccessSize ;;R parse_label.
86+
87+
Definition parse_rip_relative_kind : ParserAction rip_relative_kind
88+
:= parse_map (fun _ => explicitly_rip_relative) "rip".
8589

86-
Definition parse_MEM : ParserAction MEM
90+
Definition parse_MEM {opts : assembly_program_options} : ParserAction MEM
8791
:= parse_option_list_map
88-
(fun '(access_size, (constant_location_label, (br (*base reg*), sr (*scale reg, including z *), offset, base_label)))
92+
(fun '(access_size, (constant_location_label, (br (*base reg*), sr (*scale reg, including z *), offset, base_label, rip_relative)))
8993
=> match base_label, constant_location_label with
9094
| Some _, Some _ => (* invalid? *) None
9195
| Some _ as lbl, None
@@ -96,14 +100,15 @@ Definition parse_MEM : ParserAction MEM
96100
; mem_base_reg := br:option REG
97101
; mem_base_label := lbl
98102
; mem_scale_reg := sr:option (Z * REG)
99-
; mem_offset := offset:option Z |}
103+
; mem_offset := offset:option Z
104+
; rip_relative := rip_relative:rip_relative_kind |}
100105
end)
101106
(((strip_whitespace_after parse_AccessSize)?) ;;
102107
(parse_non_access_size_label?) ;;
103108
(parse_option_list_map
104109
(fun '(offset, vars)
105110
=> (vars <-- List.map (fun '(c, (v, e), vs) => match vs, e with [], 1%Z => Some (c, v) | _, _ => None end) vars;
106-
let regs : list (Z * REG) := Option.List.map (fun '(c, v) => match v with inl v => Some (c, v) | inr _ => None end) vars in
111+
let regs : list (Z * (REG + rip_relative_kind)) := Option.List.map (fun '(c, v) => match v with inl v => Some (c, v) | inr _ => None end) vars in
107112
let labels : list (Z * string) := Option.List.map (fun '(c, v) => match v with inr v => Some (c, v) | inl _ => None end) vars in
108113
base_label <- match labels with
109114
| [] => Some None
@@ -114,15 +119,17 @@ Definition parse_MEM : ParserAction MEM
114119
base_scale_reg <- match regs with
115120
| [] => Some (None, None)
116121
| [(1%Z, r)] => Some (Some r, None)
117-
| [(s, r)] => Some (None, Some (s, r))
118-
| [(1%Z, r1); (s, r2)]
119-
| [(s, r2); (1%Z, r1)]
122+
| [(s, inl r)] => Some (None, Some (s, r))
123+
| [(1%Z, r1); (s, inl r2)]
124+
| [(s, inl r2); (1%Z, r1)]
120125
=> Some (Some r1, Some (s, r2))
121126
| _ => None
122127
end;
123128
let '(br, sr) := base_scale_reg in
124-
Some (br (*base reg*), sr (*scale reg, including z *), offset, base_label))%option)
125-
("[" ;;R parse_Z_poly_strict (sum_beq _ _ REG_beq String.eqb) (parse_or_else_gen (fun x => x) parse_REG parse_label) ;;L "]"))).
129+
let rip_relative := match br with Some (inr k) => k | _ => if default_rel then implicitly_rip_relative else not_rip_relative end in
130+
let br := (br <- br; match br with inl br => Some br | inr _ => None end)%option in
131+
Some (br (*base reg*), sr (*scale reg, including z *), offset, base_label, rip_relative))%option)
132+
("[" ;;R parse_Z_poly_strict (sum_beq _ _ (sum_beq _ _ REG_beq rip_relative_kind_beq) String.eqb) (parse_or_else_gen (fun x => x) (parse_or_else_gen (fun x => x) parse_REG parse_rip_relative_kind) parse_label) ;;L "]"))).
126133

127134
Definition parse_CONST (const_keyword : bool) : ParserAction CONST
128135
:= if const_keyword
@@ -138,7 +145,7 @@ Definition parse_JUMP_LABEL : ParserAction JUMP_LABEL
138145
((strip_whitespace_after "NEAR ")? ;; parse_label).
139146

140147
(* we only parse something as a label if it cannot possibly be anything else, because asm is terrible and has ambiguous parses otherwise :-( *)
141-
Definition parse_ARG (const_keyword : bool) : ParserAction ARG
148+
Definition parse_ARG {opts : assembly_program_options} (const_keyword : bool) : ParserAction ARG
142149
:= parse_or_else
143150
(parse_alt_list
144151
[parse_map reg parse_REG
@@ -170,7 +177,7 @@ Definition parse_OpPrefix : ParserAction OpPrefix
170177
:= parse_strs parse_OpPrefix_list.
171178

172179
(** assumes no leading nor trailing whitespace and no comment *)
173-
Definition parse_RawLine : ParserAction RawLine
180+
Definition parse_RawLine {opts : assembly_program_options} : ParserAction RawLine
174181
:= fun s => (
175182
let s := String.trim s in
176183
(* get the first space-separated opcode *)
@@ -225,7 +232,7 @@ Definition parse_RawLine : ParserAction RawLine
225232
parsed_prefix
226233
end)%bool.
227234

228-
Definition parse_Line (line_num : N) : ParserAction Line
235+
Definition parse_Line {opts : assembly_program_options} (line_num : N) : ParserAction Line
229236
:= fun s
230237
=> let '(indentv, rest_linev) := take_while_drop_while Ascii.is_whitespace s in
231238
let '(precommentv, commentv)
@@ -242,22 +249,35 @@ Definition parse_Line (line_num : N) : ParserAction Line
242249
(parse_RawLine rawlinev).
243250

244251
(* the error is the unparsable lines *)
245-
Fixpoint parse_Lines' (l : list string) (line_num : N) : ErrorT (list string) Lines
252+
Fixpoint parse_Lines' {opts : assembly_program_options} (l : list string) (line_num : N) : ErrorT (list string) Lines
246253
:= match l with
247254
| [] => Success []
248255
| l :: ls
249-
=> match finalize (parse_Line line_num) l, parse_Lines' ls (line_num + 1) with
256+
=> let '(result, next_opts) :=
257+
match finalize (@parse_Line opts line_num) l with
258+
| None => (None, opts)
259+
| Some result =>
260+
(Some result,
261+
match result.(rawline) with
262+
| DEFAULT_REL => {| default_rel := true |}
263+
| _ => opts
264+
end)
265+
end in
266+
match result, @parse_Lines' next_opts ls (line_num + 1) with
250267
| None, Error ls => Error (("Line " ++ show line_num ++ ": " ++ l) :: ls)
251268
| None, Success _ => Error (("Line " ++ show line_num ++ ": " ++ l) :: nil)
252269
| Some _, Error ls => Error ls
253270
| Some l, Success ls => Success (l :: ls)
254271
end
255272
end.
256273

257-
Definition parse_Lines (l : list string) : ErrorT (list string) Lines
274+
Definition parse_Lines {opts : assembly_program_options} (l : list string) : ErrorT (list string) Lines
258275
:= parse_Lines' (String.split_newlines l) 1.
259276

260-
Notation parse := parse_Lines (only parsing).
277+
#[export] Instance default_assembly_program_options : assembly_program_options
278+
:= {| default_rel := false |}.
279+
280+
Notation parse := (@parse_Lines default_assembly_program_options) (only parsing).
261281

262282
Global Instance show_lvl_MEM : ShowLevel MEM
263283
:= fun m
@@ -274,11 +294,19 @@ Global Instance show_lvl_MEM : ShowLevel MEM
274294
| None => show_lvl
275295
end)
276296
(fun 'tt
277-
=> let reg_part
278-
:= (match m.(mem_base_reg), m.(mem_scale_reg) with
279-
| (*"[Reg]" *) Some br, None => show_REG br
280-
| (*"[Reg + Z * Reg]"*) Some br, Some (z, sr) => show_REG br ++ " + " ++ Decimal.show_Z z ++ " * " ++ show_REG sr (*only matching '+' here, because there cannot be a negative scale. *)
281-
| (*"[ Z * Reg]"*) None, Some (z, sr) => Decimal.show_Z z ++ " * " ++ show_REG sr
297+
=> let is_explict_rip_relative := match m.(rip_relative) with explicitly_rip_relative => true | _ => false end in
298+
let base_reg_str :=
299+
match is_explict_rip_relative, m.(mem_base_reg) with
300+
| false, Some br => Some (show_REG br)
301+
| false, None => None
302+
| true, None => Some "rip"
303+
| true, Some br => Some ("rip + " ++ show_REG br) (* but this should not happen *)
304+
end in
305+
let reg_part
306+
:= (match base_reg_str, m.(mem_scale_reg) with
307+
| (*"[Reg]" *) Some br, None => br
308+
| (*"[Reg + Z * Reg]"*) Some br, Some (z, sr) => br ++ " + " ++ Decimal.show_Z z ++ " * " ++ show_REG sr (*only matching '+' here, because there cannot be a negative scale. *)
309+
| (*"[ Z * Reg]"*) None, Some (z, sr) => Decimal.show_Z z ++ " * " ++ show_REG sr
282310
| (*"[ ]"*) None, None => "" (* impossible, because only offset is invalid, but we seem to need it for coq because both are option's*)
283311
end%Z) in
284312
let offset_part
@@ -291,8 +319,8 @@ Global Instance show_lvl_MEM : ShowLevel MEM
291319
then "0x08 * " ++ Decimal.show_Z (offset / 8)
292320
else Hex.show_Z offset)
293321
end%Z) in
294-
match m.(mem_base_label), m.(mem_base_reg), m.(mem_offset), m.(mem_scale_reg) with
295-
| Some lbl, Some rip, None, None => lbl ++ "[" ++ reg_part ++ offset_part ++ "]"
322+
match m.(mem_base_label), is_explict_rip_relative, m.(mem_offset), m.(mem_scale_reg) with
323+
| Some lbl, true, None, None => lbl ++ "[" ++ reg_part ++ offset_part ++ "]"
296324
| Some lbl, _, _, _ => let l_offset := lbl ++ offset_part in
297325
"[" ++
298326
(if reg_part =? ""
@@ -603,9 +631,9 @@ Definition get_labeled_data (ls : Lines) : list (string * list (AccessSize * lis
603631
let labeled_data := List.filter (fun '(_, data) => match data with nil => false | _ => true end) labeled_data in
604632
labeled_data.
605633

606-
Definition parse_assembly_options (ls : Lines) : assembly_program_options
634+
(* Definition parse_assembly_options (ls : Lines) : assembly_program_options
607635
:= {| default_rel := Option.is_Some (List.find (fun l => match l.(rawline) with
608636
| DEFAULT_REL => true
609637
| _ => false
610638
end) ls)
611-
|}.
639+
|}. *)

src/Assembly/Syntax.v

+13-2
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,21 @@ Coercion bits_of_AccessSize (x : AccessSize) : N
5353
| qword => 64
5454
end.
5555

56-
Record MEM := { mem_bits_access_size : option AccessSize ; mem_base_reg : option REG ; mem_scale_reg : option (Z * REG) ; mem_base_label : option string ; mem_offset : option Z }.
56+
Local Set Boolean Equality Schemes.
57+
Local Set Decidable Equality Schemes.
58+
Variant rip_relative_kind := explicitly_rip_relative | implicitly_rip_relative | not_rip_relative.
59+
Local Unset Boolean Equality Schemes.
60+
Local Unset Decidable Equality Schemes.
61+
Global Coercion bool_of_rip_relative_kind (x : rip_relative_kind) : bool :=
62+
match x with
63+
| explicitly_rip_relative => true
64+
| implicitly_rip_relative => true
65+
| not_rip_relative => false
66+
end.
67+
Record MEM := { mem_bits_access_size : option AccessSize ; mem_base_reg : option REG ; mem_scale_reg : option (Z * REG) ; mem_base_label : option string ; mem_offset : option Z ; rip_relative : rip_relative_kind }.
5768

5869
Definition mem_of_reg (r : REG) : MEM :=
59-
{| mem_base_reg := Some r ; mem_offset := None ; mem_scale_reg := None ; mem_bits_access_size := None ; mem_base_label := None |}.
70+
{| mem_base_reg := Some r ; mem_offset := None ; mem_scale_reg := None ; mem_bits_access_size := None ; mem_base_label := None ; rip_relative := not_rip_relative |}.
6071

6172
Inductive FLAG := CF | PF | AF | ZF | SF | OF.
6273

0 commit comments

Comments
 (0)