Files
mercury/compiler/x86_64_instrs.m
Fransiska Nathania Handoko 8ad3d82efb Add a mechanism for mapping between MVM registers and x86_64 registers
Estimated hours taken: 40
Branches: main

Add a mechanism for mapping between MVM registers and x86_64 registers
or fake reg array slots. This is needed by the x86_64 asm code generator.
At the momment the mapping is the same as that used by (non .par) grades
that use global register variables on the x86_64.

Modify the x86_64 code generator to emit additional instructions to
perform loads and store to the fake ref array when a virtual machine
registers is not mapped to a physical register.

compiler/x86_64_regs.m:
	Defines a mapping of MVM registers to x86_64 registers.

compiler/x86_64_out.m:
	Fixes some predicates to output asm codes to conform with the changes
	in x86_64_instrs.m.

compiler/llds_to_x86_64.m:
	Applies the register mapping during the code generation.

	Add instructions to access values in the fake reg array.
	(XXX this is still incomplete).

compiler/x86_64_instrs.m:
	Adds discriminated union types and fixes some predicates to make the code
	easier for modification.

compiler/mercury_compile.m:
	Imports x86_64_regs and x86_64_out modules.

compiler/ll_backend.m:
	Includes the x86_64_regs module.


Index: compiler/ll_backend.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/ll_backend.m,v
retrieving revision 1.19
diff -u -r1.19 ll_backend.m
--- compiler/ll_backend.m	5 Feb 2007 22:30:32 -0000	1.19
+++ compiler/ll_backend.m	9 Feb 2007 04:57:45 -0000
@@ -97,6 +97,7 @@
 :- include_module llds_to_x86_64_out.
 :- include_module x86_64_instrs.
 :- include_module x86_64_out.
+:- include_module x86_64_regs.

 :- implementation.

Index: compiler/llds_to_x86_64.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/llds_to_x86_64.m,v
retrieving revision 1.1
diff -u -r1.1 llds_to_x86_64.m
--- compiler/llds_to_x86_64.m	5 Feb 2007 22:30:32 -0000	1.1
+++ compiler/llds_to_x86_64.m	19 Feb 2007 06:10:47 -0000
@@ -10,7 +10,13 @@
 % Main author: fhandoko.
 %
 % This module implements the LLDS->x86_64 asm code generator.
-%
+%
+% NOTE:
+% 	There are a number of placeholders. It appears as a string like this:
+% 	<<placeholder>>. The code generator places them as either x86_64_comment
+% 	or operand_label type. For example:
+% 		x86_64_comment("<<placeholder>>") or
+% 		operand_label("<<placeholder>>").
 %-----------------------------------------------------------------------------%

 :- module ll_backend.llds_to_x86_64.
@@ -37,6 +43,7 @@
 :- import_module libs.compiler_util.
 :- import_module ll_backend.llds_out.
 :- import_module ll_backend.x86_64_out.
+:- import_module ll_backend.x86_64_regs.
 :- import_module mdbcomp.prim_data.

 :- import_module bool.
@@ -72,15 +79,17 @@
 %

 llds_to_x86_64_asm(_, CProcs, AsmProcs) :-
-    transform_c_proc_list(CProcs, AsmProcs).
+    ll_backend.x86_64_regs.llds_reg_locn(RegLocnList),
+    RegMap = ll_backend.x86_64_regs.reg_map_init(RegLocnList),
+    transform_c_proc_list(RegMap, CProcs, AsmProcs).

     % Transform a list of c procedures into a list of x86_64 procedures.
     %
-:- pred transform_c_proc_list(list(c_procedure)::in,
+:- pred transform_c_proc_list(reg_map::in, list(c_procedure)::in,
     list(x86_64_procedure)::out) is det.

-transform_c_proc_list([], []).
-transform_c_proc_list([CProc | CProcs], [AsmProc | AsmProcs]) :-
+transform_c_proc_list(_, [], []).
+transform_c_proc_list(RegMap, [CProc | CProcs], [AsmProc | AsmProcs]) :-
     AsmProc0 = ll_backend.x86_64_instrs.init_x86_64_proc(CProc),
     ProcInstr0 = ll_backend.x86_64_instrs.init_x86_64_instruction,
     %
@@ -89,65 +98,64 @@
     %
     ProcStr = backend_libs.name_mangle.proc_label_to_c_string(
         AsmProc0 ^ x86_64_proc_label, no),
-    ProcName = x86_64_directive(x86_64_pseudo_type(ProcStr, "@function")),
+    ProcName = x86_64_directive(x86_64_pseudo_type(ProcStr, function)),
     ProcInstr = ProcInstr0 ^ x86_64_inst := [ProcName],
-    transform_c_instr_list(CProc ^ cproc_code, AsmInstr0),
+    transform_c_instr_list(RegMap, CProc ^ cproc_code, AsmInstr0),
     list.append([ProcInstr], AsmInstr0, AsmInstr),
     AsmProc = AsmProc0 ^ x86_64_code := AsmInstr,
-    transform_c_proc_list(CProcs, AsmProcs).
+    transform_c_proc_list(RegMap, CProcs, AsmProcs).

     % Transform a list of c instructions into a list of x86_64 instructions.
     %
-:- pred transform_c_instr_list(list(instruction)::in,
+:- pred transform_c_instr_list(reg_map::in, list(instruction)::in,
     list(x86_64_instruction)::out) is det.

-transform_c_instr_list([], []).
-transform_c_instr_list([CInstr0 | CInstr0s], [AsmInstr | AsmInstrs]) :-
+transform_c_instr_list(_, [], []).
+transform_c_instr_list(RegMap, [CInstr0 | CInstr0s], [AsmInstr | AsmInstrs]) :-
     CInstr0 = llds_instr(CInstr1, Comment),
-    instr_to_x86_64(CInstr1, AsmInstrList),
+    instr_to_x86_64(RegMap, CInstr1, RegMap1, AsmInstrList),
+    ll_backend.x86_64_regs.reg_map_reset_scratch_reg_info(RegMap1, RegMap2),
     AsmInstr0 = ll_backend.x86_64_instrs.init_x86_64_instruction,
     AsmInstr1 = AsmInstr0 ^ x86_64_inst := AsmInstrList,
     AsmInstr = AsmInstr1 ^ x86_64_inst_comment := Comment,
-    transform_c_instr_list(CInstr0s, AsmInstrs).
+    transform_c_instr_list(RegMap2, CInstr0s, AsmInstrs).

     % Transform a block instruction of an llds instruction into a list of
     % x86_64 instructions.
     %
-:- pred transform_block_instr(list(instruction)::in, list(x86_64_instr)::out)
-    is det.
+:- pred transform_block_instr(reg_map::in, list(instruction)::in,
+    list(x86_64_instr)::out) is det.

-transform_block_instr(CInstrs, Instrs) :-
-    transform_block_instr_list(CInstrs, ListInstrs),
+transform_block_instr(RegMap, CInstrs, Instrs) :-
+    transform_block_instr_list(RegMap, CInstrs, ListInstrs),
     list.condense(ListInstrs, Instrs).

-:- pred transform_block_instr_list(list(instruction)::in,
+:- pred transform_block_instr_list(reg_map::in, list(instruction)::in,
     list(list(x86_64_instr))::out) is det.

-transform_block_instr_list([], []).
-transform_block_instr_list([CInstr0 | CInstr0s], [Instr0 | Instr0s]) :-
+transform_block_instr_list(_, [], []).
+transform_block_instr_list(RegMap, [CInstr0 | CInstr0s], [Instr0 | Instr0s]) :-
     CInstr0 = llds_instr(CInstr, _),
-    instr_to_x86_64(CInstr, Instr0),
-    transform_block_instr_list(CInstr0s, Instr0s).
+    instr_to_x86_64(RegMap, CInstr, RegMap1, Instr0),
+    ll_backend.x86_64_regs.reg_map_reset_scratch_reg_info(RegMap1, RegMap2),
+    transform_block_instr_list(RegMap2, CInstr0s, Instr0s).

     % Transform livevals of llds instruction into a list of x86_64 instructions.
     %
-:- pred transform_livevals(list(lval)::in, list(x86_64_instr)::out) is det.
+:- pred transform_livevals(reg_map::in, list(lval)::in, list(x86_64_instr)::out)
+    is det.

-transform_livevals([], []).
-transform_livevals([Lval | Lvals], [Instr | Instrs]) :-
-    transform_lval(Lval, Res0, Res1),
+transform_livevals(_, [], []).
+transform_livevals(RegMap, [Lval | Lvals], [Instr | Instrs]) :-
+    transform_lval(RegMap, Lval, RegMap1, Res0, Res1),
     (
-        Res0 = yes(LvalOp),
-        Instr = x86_64_instr(mov(operand_label("<<livevals>>"), LvalOp)),
-        transform_livevals(Lvals, Instrs)
+        Res0 = yes(LvalOp)
     ;
         Res0 = no,
         (
             Res1 = yes(LvalInstrs),
             ( get_last_instr_opand(LvalInstrs, LastOp) ->
-                Instr = x86_64_instr(mov(operand_label("<<livevals>>"),
-                    LastOp)),
-                transform_livevals(Lvals, Instrs)
+                LvalOp = LastOp
             ;
                 unexpected(this_file, "transform_livevals: unexpected:"
                     ++ " get_last_instr_opand failed")
@@ -157,22 +165,25 @@
             unexpected(this_file, "transform_livevals: unexpected:"
                 ++ " get_last_instr_opand failed")
         )
-    ).
+    ),
+    Instr = x86_64_instr(mov(operand_label("<<liveval>>"), LvalOp)),
+    transform_livevals(RegMap1, Lvals, Instrs).

     % Given an llds instruction, transform it into equivalent x86_64
     % instructions.
     %
-:- pred instr_to_x86_64(instr::in, list(x86_64_instr)::out) is det.
+:- pred instr_to_x86_64(reg_map::in, instr::in, reg_map::out,
+    list(x86_64_instr)::out) is det.

-instr_to_x86_64(comment(Comment), [x86_64_comment(Comment)]).
-instr_to_x86_64(livevals(RegsAndStackLocs), Instrs) :-
+instr_to_x86_64(RegMap, comment(Comment), RegMap, [x86_64_comment(Comment)]).
+instr_to_x86_64(RegMap, livevals(RegsAndStackLocs), RegMap, Instrs) :-
     set.to_sorted_list(RegsAndStackLocs, List),
-    transform_livevals(List, Instrs).
-instr_to_x86_64(block(_, _, CInstrs), Instrs) :-
-    transform_block_instr(CInstrs, Instrs).
-instr_to_x86_64(assign(Lval, Rval), Instrs) :-
-    transform_lval(Lval, Res0, Res1),
-    transform_rval(Rval, Res2, Res3),
+    transform_livevals(RegMap, List, Instrs).
+instr_to_x86_64(RegMap, block(_, _, CInstrs), RegMap, Instrs) :-
+    transform_block_instr(RegMap, CInstrs, Instrs).
+instr_to_x86_64(RegMap0, assign(Lval, Rval), RegMap, Instrs) :-
+    transform_lval(RegMap0, Lval, RegMap1, Res0, Res1),
+    transform_rval(RegMap1, Rval, RegMap, Res2, Res3),
     (
         Res0 = yes(LvalOp),
         (
@@ -184,7 +195,7 @@
                 Res3 = yes(RvalInstrs),
                 ( get_last_instr_opand(RvalInstrs, LastOp) ->
                     LastInstr = x86_64_instr(mov(LastOp, LvalOp)),
-                    list.append(RvalInstrs, [LastInstr], Instrs)
+                    Instrs = RvalInstrs ++ [LastInstr]
                 ;
                     unexpected(this_file, "instr_to_x86_64: assign: unexpected:"
                         ++ " get_last_instr_opand failed")
@@ -202,16 +213,15 @@
             ( get_last_instr_opand(LvalInstrs, LvalLastOp) ->
                 (
                     Res2 = yes(RvalOp),
-                    Instr0 = x86_64_instr(mov(RvalOp, LvalLastOp)),
-                    list.append(LvalInstrs, [Instr0], Instrs)
-
+                    Instr1 = x86_64_instr(mov(RvalOp, LvalLastOp)),
+                    Instrs = LvalInstrs ++ [Instr1]
                 ;
                     Res2 = no,
                     (
                         Res3 = yes(RvalInstrs),
                         ( get_last_instr_opand(RvalInstrs, RvalLastOp) ->
-                            Instr0 = x86_64_instr(mov(RvalLastOp, LvalLastOp)),
-                            Instrs = LvalInstrs ++ [Instr0] ++ RvalInstrs
+                            Instr1 = x86_64_instr(mov(RvalLastOp, LvalLastOp)),
+                            Instrs = LvalInstrs ++ RvalInstrs ++ [Instr1]
                         ;
                             unexpected(this_file, "instr_to_x86_64: assign:"
                                 ++ " unexpected:get_last_instr_opand failed")
@@ -232,22 +242,42 @@
                 ++ "Lval")
         )
     ).
-instr_to_x86_64(llcall(Target0, Continuation0, _, _, _, _), Instrs) :-
+instr_to_x86_64(RegMap0, llcall(Target0, Continuation0, _, _, _, _), RegMap,
+        Instrs) :-
     code_addr_type(Target0, Target1),
     code_addr_type(Continuation0, Continuation1),
-    Instr1 = x86_64_instr(mov(operand_label(Continuation1),
-        operand_label("<<succip>>"))),
+    lval_reg_locn(RegMap0, succip, Op0, Instr0),
+    ll_backend.x86_64_regs.reg_map_remove_scratch_reg(RegMap0, RegMap),
+    (
+        Op0 = yes(Op)
+    ;
+        Op0 = no,
+        (
+            Instr0 = yes(Instr),
+            ( get_last_instr_opand(Instr, LastOpand) ->
+                Op = LastOpand
+            ;
+                unexpected(this_file, "instr_to_x86_64: llcall: unexpected:"
+                    ++ " get_last_instr_opand failed")
+            )
+        ;
+            Instr0 = no,
+            unexpected(this_file, "instr_to_x86_64: llcall: unexpected:" ++
+               " lval_reg_locn failed")
+        )
+    ),
+    Instr1 = x86_64_instr(mov(operand_label(Continuation1), Op)),
     Instr2 = x86_64_instr(jmp(operand_label(Target1))),
     Instrs = [Instr1, Instr2].
-instr_to_x86_64(mkframe(_, _), [x86_64_comment("<<mkframe>>")]).
-instr_to_x86_64(label(Label), Instrs) :-
+instr_to_x86_64(RegMap, mkframe(_, _), RegMap, [x86_64_comment("<<mkframe>>")]).
+instr_to_x86_64(RegMap, label(Label), RegMap, Instrs) :-
     LabelStr = ll_backend.llds_out.label_to_c_string(Label, no),
     Instrs = [x86_64_label(LabelStr)].
-instr_to_x86_64(goto(CodeAddr), Instrs) :-
+instr_to_x86_64(RegMap, goto(CodeAddr), RegMap, Instrs) :-
     code_addr_type(CodeAddr, Label),
     Instrs = [x86_64_instr(jmp(operand_label(Label)))].
-instr_to_x86_64(computed_goto(Rval, Labels), Instrs) :-
-    transform_rval(Rval, Res0, Res1),
+instr_to_x86_64(RegMap0, computed_goto(Rval, Labels), RegMap, Instrs) :-
+    transform_rval(RegMap0, Rval, RegMap1, Res0, Res1),
     (
         Res0 = yes(RvalOp),
         RvalInstrs = []
@@ -268,17 +298,19 @@
         )
     ),
     labels_to_string(Labels, "", LabelStr),
-    TempReg = operand_reg(gp_reg(13)),
+    ScratchReg = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap1),
+    ll_backend.x86_64_regs.reg_map_remove_scratch_reg(RegMap1, RegMap),
+    TempReg = operand_reg(ScratchReg),
     Instr0 = x86_64_instr(mov(operand_mem_ref(mem_abs(base_expr(LabelStr))),
         TempReg)),
     Instr1 = x86_64_instr(add(RvalOp, TempReg)),
     Instr2 = x86_64_instr(jmp(TempReg)),
     Instrs = RvalInstrs ++ [Instr0] ++ [Instr1] ++ [Instr2].
-instr_to_x86_64(arbitrary_c_code(_, _, _), Instrs) :-
+instr_to_x86_64(RegMap, arbitrary_c_code(_, _, _), RegMap, Instrs) :-
     Instrs = [x86_64_comment("<<arbitrary_c_code>>")].
-instr_to_x86_64(if_val(Rval, CodeAddr), Instrs) :-
+instr_to_x86_64(RegMap0, if_val(Rval, CodeAddr), RegMap, Instrs) :-
     code_addr_type(CodeAddr, Target),
-    transform_rval(Rval, Res0, Res1),
+    transform_rval(RegMap0, Rval, RegMap, Res0, Res1),
     (
         Res0 = yes(RvalOp)
     ;
@@ -297,14 +329,17 @@
                 ++ " Rval")
         )
     ),
-    ll_backend.x86_64_out.operand_type(RvalOp, RvalStr),
+    ll_backend.x86_64_out.operand_to_string(RvalOp, RvalStr),
     Instrs = [x86_64_directive(x86_64_pseudo_if(RvalStr)), x86_64_instr(j(
         operand_label(Target), e)), x86_64_directive(endif)].
-instr_to_x86_64(save_maxfr(_), [x86_64_comment("<<save_maxfr>>")]).
-instr_to_x86_64(restore_maxfr(_), [x86_64_comment("<<restore_maxfr>>")]).
-instr_to_x86_64(incr_hp(Lval, Tag0, Words0, Rval, _, _), Instrs) :-
-    transform_rval(Rval, Res0, Res1),
-    transform_lval(Lval, Res2, Res3),
+instr_to_x86_64(RegMap, save_maxfr(_), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<save_maxfr>>")].
+instr_to_x86_64(RegMap, restore_maxfr(_), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<restore_maxfr>>")].
+instr_to_x86_64(RegMap0, incr_hp(Lval, Tag0, Words0, Rval, _, _), RegMap,
+        Instrs) :-
+    transform_rval(RegMap0, Rval, RegMap1, Res0, Res1),
+    transform_lval(RegMap1, Lval, RegMap2, Res2, Res3),
     (
         Res0 = yes(RvalOp)
     ;
@@ -344,14 +379,17 @@
     (
         Words0 = yes(Words),
         IncrVal = operand_imm(imm32(int32(Words))),
-        TempReg = operand_reg(gp_reg(13)),
-        ll_backend.x86_64_out.operand_type(RvalOp, RvalStr),
+        ScratchReg0 = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap2),
+        reg_map_remove_scratch_reg(RegMap2, RegMap3),
+        TempReg1 = operand_reg(ScratchReg0),
+        ll_backend.x86_64_out.operand_to_string(RvalOp, RvalStr),
         MemRef = operand_mem_ref(mem_abs(base_expr(RvalStr))),
-        LoadAddr = x86_64_instr(lea(MemRef, TempReg)),
-        IncrAddInstr = x86_64_instr(add(IncrVal, TempReg)),
+        LoadAddr = x86_64_instr(lea(MemRef, TempReg1)),
+        IncrAddInstr = x86_64_instr(add(IncrVal, TempReg1)),
         list.append([LoadAddr], [IncrAddInstr], IncrAddrInstrs)
     ;
         Words0 = no,
+        RegMap3 = RegMap2,
         IncrAddrInstrs = []
     ),
     (
@@ -360,86 +398,141 @@
         Tag0 = no,
         Tag = 0
     ),
-    TempReg = operand_reg(gp_reg(13)),
-    ImmToReg = x86_64_instr(mov(RvalOp, TempReg)),
-    SetTag = x86_64_instr(or(operand_imm(imm32(int32(Tag))), TempReg)),
-    Instr0 = x86_64_instr(mov(TempReg, LvalOp)),
-    Instrs = IncrAddrInstrs ++ [ImmToReg] ++ [SetTag] ++ [Instr0].
-instr_to_x86_64(mark_hp(_), [x86_64_comment("<<mark_hp>>")]).
-instr_to_x86_64(restore_hp(_), [x86_64_comment("<<restore_hp>>")]).
-instr_to_x86_64(free_heap(_), [x86_64_comment("<<free_heap>>")]).
-instr_to_x86_64(store_ticket(_), [x86_64_comment("<<store_ticket>>")]).
-instr_to_x86_64(reset_ticket(_, _), [x86_64_comment("<<reset_ticket>>")]).
-instr_to_x86_64(prune_ticket, [x86_64_comment("<<prune_ticket>>")]).
-instr_to_x86_64(discard_ticket, [x86_64_comment("<<discard_ticket>>")]).
-instr_to_x86_64(mark_ticket_stack(_), [x86_64_comment("<<mark_ticket_stack>>")]).
-instr_to_x86_64(prune_tickets_to(_), [x86_64_comment("<<prune_tickets_to>>")]).
-instr_to_x86_64(incr_sp(NumSlots, ProcName, _), Instrs) :-
+    ScratchReg1 = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap3),
+    reg_map_remove_scratch_reg(RegMap3, RegMap),
+    TempReg2 = operand_reg(ScratchReg1),
+    ImmToReg = x86_64_instr(mov(RvalOp, TempReg2)),
+    SetTag = x86_64_instr(or(operand_imm(imm32(int32(Tag))), TempReg2)),
+    Instr1 = x86_64_instr(mov(TempReg2, LvalOp)),
+    Instrs = IncrAddrInstrs ++ [ImmToReg] ++ [SetTag] ++ [Instr1].
+instr_to_x86_64(RegMap, mark_hp(_), RegMap, [x86_64_comment("<<mark_hp>>")]).
+instr_to_x86_64(RegMap, restore_hp(_), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<restore_hp>>")].
+instr_to_x86_64(RegMap, free_heap(_), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<free_heap>>")].
+instr_to_x86_64(RegMap, store_ticket(_), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<store_ticket>>")].
+instr_to_x86_64(RegMap, reset_ticket(_, _), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<reset_ticket>>")].
+instr_to_x86_64(RegMap, prune_ticket, RegMap, Instr) :-
+    Instr = [x86_64_comment("<<prune_ticket>>")].
+instr_to_x86_64(RegMap, discard_ticket, RegMap, Instr) :-
+    Instr = [x86_64_comment("<<discard_ticket>>")].
+instr_to_x86_64(RegMap, mark_ticket_stack(_), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<mark_ticket_stack>>")].
+instr_to_x86_64(RegMap, prune_tickets_to(_), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<prune_tickets_to>>")].
+instr_to_x86_64(RegMap, incr_sp(NumSlots, ProcName, _), RegMap, Instrs) :-
     Instr1 = x86_64_comment("<<incr_sp>> " ++ ProcName),
     Instr2 = x86_64_instr(enter(uint16(NumSlots), uint8(0))),
     Instrs = [Instr1, Instr2].
-instr_to_x86_64(decr_sp(NumSlots), Instrs) :-
+instr_to_x86_64(RegMap0, decr_sp(NumSlots), RegMap, Instrs) :-
     DecrOp = operand_imm(imm32(int32(NumSlots))),
-    Instr = x86_64_instr(sub(DecrOp, operand_reg(gp_reg(13)))),
+    ScratchReg = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap0),
+    ll_backend.x86_64_regs.reg_map_remove_scratch_reg(RegMap0, RegMap),
+    Instr = x86_64_instr(sub(DecrOp, operand_reg(ScratchReg))),
     list.append([x86_64_comment("<<decr_sp>> ")], [Instr], Instrs).
-instr_to_x86_64(decr_sp_and_return(NumSlots), Instrs) :-
+instr_to_x86_64(RegMap, decr_sp_and_return(NumSlots), RegMap, Instrs) :-
     Instrs = [x86_64_comment("<<decr_sp_and_return>> " ++
         string.int_to_string(NumSlots))].
-instr_to_x86_64(foreign_proc_code(_, _, _, _, _, _, _, _, _),
-    [x86_64_comment("<<foreign_proc_code>>")]).
-instr_to_x86_64(init_sync_term(_, _), [x86_64_comment("<<init_sync_term>>")]).
-instr_to_x86_64(fork(_), [x86_64_comment("<<fork>>")]).
-instr_to_x86_64(join_and_continue(_, _), [x86_64_comment("<<join_and_continue>>")]).
-
+instr_to_x86_64(RegMap, foreign_proc_code(_, _, _, _, _, _, _, _, _), RegMap,
+        Instr) :-
+    Instr = [x86_64_comment("<<foreign_proc_code>>")].
+instr_to_x86_64(RegMap, init_sync_term(_, _), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<init_sync_term>>")].
+instr_to_x86_64(RegMap, fork(_), RegMap, [x86_64_comment("<<fork>>")]).
+instr_to_x86_64(RegMap, join_and_continue(_, _), RegMap, Instr) :-
+    Instr = [x86_64_comment("<<join_and_continue>>")].

     % Transform lval into either an x86_64 operand or x86_64 instructions.
     %
-:- pred transform_lval(lval::in, maybe(operand)::out,
+:- pred transform_lval(reg_map::in, lval::in, reg_map::out, maybe(operand)::out,
     maybe(list(x86_64_instr))::out) is det.

-transform_lval(reg(CReg, CRegNum), Op, no) :-
-    (
+transform_lval(RegMap0, reg(CReg, CRegNum), RegMap, Op, Instr) :-
+    (
         CReg = reg_r,
-        Op = yes(operand_reg(gp_reg(CRegNum)))
+        lval_reg_locn(RegMap, reg(CReg, CRegNum), Op, Instr),
+        reg_map_remove_scratch_reg(RegMap0, RegMap)
     ;
         CReg = reg_f,
-        Op = no
+        unexpected(this_file, "transform_lval: unexpected: llds reg_f")
     ).
-transform_lval(succip, yes(operand_label("<<succip>>")), no).
-transform_lval(maxfr, yes(operand_label("<<maxfr>>")), no).
-transform_lval(curfr, yes(operand_label("<<curfr>>")), no).
-transform_lval(hp, yes(operand_label("<<hp>>")), no).
-transform_lval(sp, yes(operand_label("<<sp>>")), no).
-transform_lval(parent_sp, yes(operand_label("<<parent_sp>>")), no).
-transform_lval(temp(CReg, CRegNum), Op, no) :-
-    (
-        CReg = reg_r,
-        Op = yes(operand_reg(gp_reg(CRegNum)))
+transform_lval(RegMap0, succip, RegMap, Op, Instr) :-
+    lval_reg_locn(RegMap0, succip, Op, Instr),
+    reg_map_remove_scratch_reg(RegMap0, RegMap).
+transform_lval(RegMap0, maxfr, RegMap, Op, Instr) :-
+    lval_reg_locn(RegMap0, maxfr, Op, Instr),
+    reg_map_remove_scratch_reg(RegMap0, RegMap).
+transform_lval(RegMap0, curfr, RegMap, Op, Instr) :-
+    lval_reg_locn(RegMap0, curfr, Op, Instr),
+    reg_map_remove_scratch_reg(RegMap0, RegMap).
+transform_lval(RegMap0, hp, RegMap, Op, Instr) :-
+    lval_reg_locn(RegMap0, hp, Op, Instr),
+    reg_map_remove_scratch_reg(RegMap0, RegMap).
+transform_lval(RegMap0, sp, RegMap, Op, Instr) :-
+    lval_reg_locn(RegMap0, sp, Op, Instr),
+    reg_map_remove_scratch_reg(RegMap0, RegMap).
+transform_lval(RegMap, parent_sp, RegMap, Op, no) :-
+    Op = yes(operand_label("<<parent_sp>>")).
+transform_lval(RegMap, temp(CReg, CRegNum), RegMap1, Op, Instr) :-
+    transform_lval(RegMap, reg(CReg, CRegNum), RegMap1, Op, Instr).
+transform_lval(RegMap0, stackvar(Offset), RegMap, Op, Instr) :-
+    RegLocn = reg_map_lookup_reg_locn(RegMap, sp),
+    ScratchReg = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap0),
+    reg_map_remove_scratch_reg(RegMap0, RegMap),
+    (
+        RegLocn = actual(Reg),
+        Op = no,
+        Instr = yes([x86_64_instr(mov(operand_mem_ref(
+            mem_abs(base_reg(Offset, Reg))), operand_reg(ScratchReg) ))])
     ;
-        CReg = reg_f,
-        Op = no
+        RegLocn = virtual(SlotNum),
+        Op = no,
+        FakeRegVal = "$" ++ "virtual_reg(" ++ string.int_to_string(SlotNum)
+            ++ ") + " ++ string.int_to_string(Offset),
+        Instr = yes([x86_64_instr(mov(
+            operand_label(FakeRegVal), operand_reg(ScratchReg)))])
     ).
-transform_lval(stackvar(Offset), Op, no) :-
-    Op = yes(operand_label(string.int_to_string(Offset) ++ "(<<stackvar>>)")).
-transform_lval(parent_stackvar(_), yes(operand_label("<<parent_stackvar>>")), no).
-transform_lval(framevar(_), yes(operand_label("<<framevar>>")), no).
-transform_lval(succip_slot(Rval), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_lval(redoip_slot(Rval), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_lval(redofr_slot(Rval), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_lval(succfr_slot(Rval), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_lval(prevfr_slot(Rval), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_lval(mem_ref(Rval), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_lval(global_var_ref(env_var_ref(Name)), yes(operand_label(Name)), no).
-transform_lval(lvar(_), yes(operand_label("<<lvar>>")), no).
-transform_lval(field(Tag0, Rval1, Rval2), no, Instrs) :-
-    transform_rval(Rval1, Res0, Res1),
-    transform_rval(Rval2, Res2, Res3),
+transform_lval(RegMap, parent_stackvar(_), RegMap, Op, no) :-
+    Op = yes(operand_label("<<parent_stackvar>>")).
+transform_lval(RegMap0, framevar(Offset), RegMap, Op, Instr) :-
+    ScratchReg= ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap),
+    reg_map_remove_scratch_reg(RegMap0, RegMap),
+    RegLocn = reg_map_lookup_reg_locn(RegMap, curfr),
+    % framevar(Int) refers to an offset Int relative to the current value of
+    % 'curfr'
+    (
+        RegLocn = actual(Reg),
+        Op = no,
+        Instr = yes([x86_64_instr(mov(operand_mem_ref(
+            mem_abs(base_reg(Offset, Reg))), operand_reg(ScratchReg) ))])
+    ;
+        RegLocn = virtual(SlotNum),
+        Op = no,
+        FakeRegVal = "$" ++ "virtual_reg(" ++ string.int_to_string(SlotNum)
+            ++ ") + " ++ string.int_to_string(Offset),
+        Instr = yes([x86_64_instr(mov(
+            operand_label(FakeRegVal), operand_reg(ScratchReg)))])
+    ).
+transform_lval(RegMap0, succip_slot(Rval), RegMap, Op, Instr) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, Instr).
+transform_lval(RegMap0, redoip_slot(Rval), RegMap, Op, Instr) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, Instr).
+transform_lval(RegMap0, redofr_slot(Rval), RegMap, Op, Instr) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, Instr).
+transform_lval(RegMap0, succfr_slot(Rval), RegMap, Op, Instr) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, Instr).
+transform_lval(RegMap0, prevfr_slot(Rval), RegMap, Op, Instr) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, Instr).
+transform_lval(RegMap0, mem_ref(Rval), RegMap, Op, Instr) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, Instr).
+transform_lval(RegMap, global_var_ref(env_var_ref(Name)), RegMap, Op, no) :-
+    Op = yes(operand_label(Name)).
+transform_lval(RegMap, lvar(_), RegMap, yes(operand_label("<<lvar>>")), no).
+transform_lval(RegMap0, field(Tag0, Rval1, Rval2), RegMap, no, Instrs) :-
+    transform_rval(RegMap0, Rval1, RegMap1, Res0, Res1),
+    transform_rval(RegMap1, Rval2, RegMap2, Res2, Res3),
     (
         Res0 = yes(RvalOp1),
         Instrs1 = []
@@ -478,8 +571,10 @@
             unexpected(this_file, "lval_instrs: field: unexpected: Rval2")
         )
     ),
-    TempReg1 = operand_reg(gp_reg(13)),
-    ll_backend.x86_64_out.operand_type(RvalOp1, RvalStr1),
+    ScratchReg = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap2),
+    reg_map_remove_scratch_reg(RegMap2, RegMap),
+    TempReg1 = operand_reg(ScratchReg),
+    ll_backend.x86_64_out.operand_to_string(RvalOp1, RvalStr1),
     MemRef = operand_mem_ref(mem_abs(base_expr(RvalStr1))),
     LoadAddr = x86_64_instr(lea(MemRef, TempReg1)),
     FieldNum = x86_64_instr(add(RvalOp2, TempReg1)),
@@ -495,14 +590,14 @@

     % Translates rval into its corresponding x86_64 operand.
     %
-:- pred transform_rval(rval::in, maybe(operand)::out, maybe(list(x86_64_instr))
-    ::out) is det.
+:- pred transform_rval(reg_map::in, rval::in, reg_map::out, maybe(operand)::out,
+    maybe(list(x86_64_instr)) ::out) is det.

-transform_rval(lval(Lval0), Op, Instrs) :-
-    transform_lval(Lval0, Op, Instrs).
-transform_rval(var(_), yes(operand_label("<<var>>")), no).
-transform_rval(mkword(Tag, Rval), no, Instrs) :-
-    transform_rval(Rval, Res0, Res1),
+transform_rval(RegMap0, lval(Lval0), RegMap, Op, Instrs) :-
+    transform_lval(RegMap0, Lval0, RegMap, Op, Instrs).
+transform_rval(RegMap, var(_), RegMap, yes(operand_label("<<var>>")), no).
+transform_rval(RegMap0, mkword(Tag, Rval), RegMap, no, Instrs) :-
+    transform_rval(RegMap0, Rval, RegMap1, Res0, Res1),
     (
         Res0 = yes(RvalOp),
         list.append([x86_64_comment("<<mkword>>")], [], Instr0)
@@ -522,27 +617,33 @@
             unexpected(this_file, "transform_rval: mkword unexpected: Rval")
         )
     ),
-    TempReg = operand_reg(gp_reg(13)),
-    ll_backend.x86_64_out.operand_type(RvalOp, RvalStr),
+    ScratchReg = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap1),
+    reg_map_remove_scratch_reg(RegMap1, RegMap),
+    TempReg = operand_reg(ScratchReg),
+    ll_backend.x86_64_out.operand_to_string(RvalOp, RvalStr),
     MemRef = operand_mem_ref(mem_abs(base_expr(RvalStr))),
     LoadAddr = x86_64_instr(lea(MemRef, TempReg)),
     SetTag = x86_64_instr(add(operand_imm(imm32(int32(Tag))), TempReg)),
     Instrs = yes(Instr0 ++ [LoadAddr] ++ [SetTag]).
-transform_rval(const(llconst_true), yes(operand_label("<<llconst_true>>")), no).
-transform_rval(const(llconst_false), yes(operand_label("<<llconst_false>>")), no).
-transform_rval(const(llconst_int(Val)), yes(operand_imm(imm32(int32(Val)))), no).
-transform_rval(const(llconst_float(_)), yes(operand_label("<<llconst_float>>")), no).
-transform_rval(const(llconst_string(String)), no, yes(Op)) :-
+transform_rval(RegMap, const(llconst_true), RegMap, Op, no) :-
+    Op = yes(operand_label("<<llconst_true>>")).
+transform_rval(RegMap, const(llconst_false), RegMap, Op, no) :-
+    Op = yes(operand_label("<<llconst_false>>")).
+transform_rval(RegMap, const(llconst_int(Val)), RegMap, Op, no) :-
+    Op = yes(operand_imm(imm32(int32(Val)))).
+transform_rval(RegMap, const(llconst_float(_)), RegMap, Op, no) :-
+    Op = yes(operand_label("<<llconst_float>>")).
+transform_rval(RegMap, const(llconst_string(String)), RegMap, no, yes(Op)) :-
     Op = [x86_64_directive(string([String]))].
-transform_rval(const(llconst_multi_string(_, _)), Op, no) :-
+transform_rval(RegMap, const(llconst_multi_string(_, _)), RegMap, Op, no) :-
     Op = yes(operand_label("<<llconst_multi_string>>")).
-transform_rval(const(llconst_code_addr(CodeAddr)), Op, no) :-
+transform_rval(RegMap, const(llconst_code_addr(CodeAddr)), RegMap, Op, no) :-
     code_addr_type(CodeAddr, CodeAddrType),
-    Op = yes(operand_label("<<llconst_code_addr>>" ++ CodeAddrType)).
-transform_rval(const(llconst_data_addr(_, _)), Op, no) :-
+    Op = yes(operand_label(CodeAddrType)).
+transform_rval(RegMap, const(llconst_data_addr(_, _)), RegMap, Op, no) :-
     Op = yes(operand_label("<<llconst_data_addr>>")).
-transform_rval(unop(Op, Rval), no, Instrs) :-
-    transform_rval(Rval, Res0, Res1),
+transform_rval(RegMap0, unop(Op, Rval), RegMap, no, Instrs) :-
+    transform_rval(RegMap0, Rval, RegMap, Res0, Res1),
     (
         Res0 = yes(_),
         unop_instrs(Op, Res0, no, Instrs0),
@@ -558,9 +659,9 @@
             unexpected(this_file, "transform_rval: unop: unexpected: Rval")
         )
     ).
-transform_rval(binop(Op, Rval1, Rval2), no, Instrs) :-
-    transform_rval(Rval1, Res1, Res2),
-    transform_rval(Rval2, Res3, Res4),
+transform_rval(RegMap0, binop(Op, Rval1, Rval2), RegMap, no, Instrs) :-
+    transform_rval(RegMap0, Rval1, RegMap1, Res1, Res2),
+    transform_rval(RegMap1, Rval2, RegMap, Res3, Res4),
     (
         Res1 = yes(Val1),
         (
@@ -606,13 +707,14 @@
             unexpected(this_file, "rval_instrs: binop: unexpected: Rval1")
         )
     ).
-transform_rval(mem_addr(stackvar_ref(Rval)), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_rval(mem_addr(framevar_ref(Rval)), Op, no) :-
-    transform_rval(Rval, Op, _).
-transform_rval(mem_addr(heap_ref(Rval1, Tag, Rval2)), no, Instrs) :-
-    transform_rval(Rval1, Res0, Res1),
-    transform_rval(Rval2, Res2, Res3),
+transform_rval(RegMap0, mem_addr(stackvar_ref(Rval)), RegMap, Op, no) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, _).
+transform_rval(RegMap0, mem_addr(framevar_ref(Rval)), RegMap, Op, no) :-
+    transform_rval(RegMap0, Rval, RegMap, Op, _).
+transform_rval(RegMap0, mem_addr(heap_ref(Rval1, Tag, Rval2)), RegMap,
+        no, Instrs) :-
+    transform_rval(RegMap0, Rval1, RegMap1, Res0, Res1),
+    transform_rval(RegMap1, Rval2, RegMap2, Res2, Res3),
     (
         Res0 = yes(Rval1Op),
         (
@@ -672,14 +774,42 @@
                 ++ " unexpected: Rval1")
        )
     ),
-    TempReg = operand_reg(gp_reg(13)),
-    ll_backend.x86_64_out.operand_type(Rval1Op, Rval1Str),
+    ScratchReg = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap2),
+    reg_map_remove_scratch_reg(RegMap2, RegMap),
+    TempReg = operand_reg(ScratchReg),
+    ll_backend.x86_64_out.operand_to_string(Rval1Op, Rval1Str),
     MemRef = operand_mem_ref(mem_abs(base_expr(Rval1Str))),
     LoadAddr = x86_64_instr(lea(MemRef, TempReg)),
     Instr0 = x86_64_instr(sub(Rval2Op, TempReg)),
     Instr1 = x86_64_instr(add(operand_imm(imm32(int32(Tag))), TempReg)),
     Instrs = yes(Instrs0 ++ [LoadAddr] ++ [Instr0] ++ [Instr1]).

+
+    % Given an llds-lval, returns either an operand or instructions (actually,
+    % it only a single move instruction. It returns a list so that the calling
+    % predicate won't have to do any rearrangements for the return value). If
+    % lval is located in an actual register, returns the actual register which
+    % corresponds to that lval. Otherwise, move lval from the fake-reg array
+    % to a temporary register.
+    %
+:- pred lval_reg_locn(reg_map::in, lval::in, maybe(operand)::out,
+    maybe(list(x86_64_instr))::out) is det.
+
+lval_reg_locn(RegMap, Lval, Op, Instr) :-
+    RegLocn = reg_map_lookup_reg_locn(RegMap, Lval),
+    (
+        RegLocn = actual(Reg),
+        Op = yes(operand_reg(Reg)),
+        Instr = no
+    ;
+        RegLocn = virtual(SlotNum),
+        Op = no,
+        FakeRegVal = "fake_reg(" ++ string.int_to_string(SlotNum) ++ ")",
+        ScratchReg = ll_backend.x86_64_regs.reg_map_get_scratch_reg(RegMap),
+        Instr = yes([x86_64_instr(mov(operand_mem_ref(mem_abs(
+                base_expr(FakeRegVal))), operand_reg(ScratchReg)))])
+    ).
+
     % x86_64 instructions for binary operation with either an operand or an
     % expression (given as a list of x86_64 instructions) or a combination of
     % both.
Index: compiler/mercury_compile.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/mercury_compile.m,v
retrieving revision 1.429
diff -u -r1.429 mercury_compile.m
--- compiler/mercury_compile.m	9 Feb 2007 04:05:16 -0000	1.429
+++ compiler/mercury_compile.m	12 Feb 2007 22:47:47 -0000
@@ -127,6 +127,8 @@
 :- import_module ll_backend.llds_to_x86_64.
 :- import_module ll_backend.llds_to_x86_64_out.
 :- import_module ll_backend.x86_64_instrs.
+:- import_module ll_backend.x86_64_out.
+:- import_module ll_backend.x86_64_regs.

     % the MLDS back-end
 :- import_module ml_backend.add_trail_ops.         % HLDS -> HLDS
Index: compiler/x86_64_instrs.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/x86_64_instrs.m,v
retrieving revision 1.2
diff -u -r1.2 x86_64_instrs.m
--- compiler/x86_64_instrs.m	5 Feb 2007 22:30:34 -0000	1.2
+++ compiler/x86_64_instrs.m	19 Feb 2007 05:57:50 -0000
@@ -11,6 +11,9 @@
 %
 % This module contains the representations of the x86_64 instructions.
 %
+% NOTE:
+% 	Instructions that make use of segment registers and string operations
+% 	(such as compare_strings) have not been implemented.
 %-----------------------------------------------------------------------------%

 :- module ll_backend.x86_64_instrs.
@@ -146,6 +149,27 @@
     ;       nle                     % Not Less or Equal (ZF = 0 and SF = OF).
     ;       g.                      % Greater (ZF = 0 and SF = OF).

+    % Optional flags argument of .section pseudo_op.
+    %
+:- type pseudo_section_flag
+    --->    a                       % section is allocatable.
+    ;       w                       % section is writable.
+    ;       x                       % section is executable.
+    ;       m                       % section is mergeable.
+    ;       s.                      % section contains zero terminated string.
+
+    % An optional type of '.section' pseudo-op.
+    %
+:- type pseudo_section_type
+    --->    progbits                % section contains data.
+    ;       nobits.                 % section does not contain data.
+
+    % type_desc field of .section pseudo-op.
+    %
+:- type pseudo_section_type_desc
+    --->    function
+    ;       object.
+
 %-----------------------------------------------------------------------------%
 %
 % x86_64 pseudo-ops.
@@ -530,8 +554,8 @@

     ;       section(
                 section_name        :: string,
-                section_flags       :: maybe(string),
-                section_type        :: maybe(string),
+                section_flags       :: maybe(list(pseudo_section_flag)),
+                section_type        :: maybe(pseudo_section_type),
                 section_entsize     :: maybe(int)
             )
             % ELF section stack manipulation directive.
@@ -618,7 +642,7 @@

     ;       x86_64_pseudo_type(
                 type_name           :: string,
-                type_desc           :: string
+                type_desc           :: pseudo_section_type_desc
             )
             % Set the type of symbol'type_name' to be either a function or an
             % object symbol.
@@ -666,8 +690,26 @@
     % General purpose registers on the x86_64.
     % Details on amd64-prog-man-vol1 manual p27.
     %
-:- type gp_reg
-    ---> gp_reg(int).
+
+:- type offset == int.
+
+:- type x86_64_reg
+    --->    rax
+    ;       rbx
+    ;       rcx
+    ;       rdx
+    ;       rbp
+    ;       rsi
+    ;       rdi
+    ;       rsp
+    ;       r8
+    ;       r9
+    ;       r10
+    ;       r11
+    ;       r12(offset)        % offset(r12) in x86_64 = offset(sp) in llds.
+    ;       r13
+    ;       r14
+    ;       r15.

     % 64-bit instruction pointer register on the x86_64. Instruction pointer
     % RIP is used as a base register for relative addressing. x86_64
@@ -719,7 +761,7 @@
 :- type base_address
     --->    base_reg(
                 base_offset             :: int,
-                base_reg                :: gp_reg
+                base_reg                :: x86_64_reg
             )

     ;       base_expr(
@@ -729,7 +771,7 @@
     % All operands for the x86_64 instructions.
     %
 :- type operand
-    --->    operand_reg(gp_reg)
+    --->    operand_reg(x86_64_reg)
     ;       operand_imm(imm_operand)
     ;       operand_mem_ref(x86_64_mem_ref)
     ;       operand_rel_offset(rel_offset)
@@ -751,7 +793,7 @@
     % signed relative offset or a label.
     %
 :- type rmrol
-    --->    rmrol_reg(gp_reg)
+    --->    rmrol_reg(x86_64_reg)
     ;       rmrol_mem_ref(x86_64_mem_ref)
     ;       rmrol_rel_offset(rel_offset)
     ;       rmrol_label(
@@ -1277,6 +1319,12 @@
             % Details on amd64-prog-man-vol3 manual p272.

 %-----------------------------------------------------------------------------%
+
+    % Returns the number of x86_64 general-purpose registers.
+    %
+:- func num_x86_64_regs = int.
+
+%-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%

 :- implementation.
@@ -1291,4 +1339,8 @@

 init_x86_64_instruction = x86_64_instr([], "").

+num_x86_64_regs = 16.
+
 %-----------------------------------------------------------------------------%
+:- end_module ll_backend.x86_64_instrs.
+%----------------------------------------------------------------------------%
Index: compiler/x86_64_out.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/x86_64_out.m,v
retrieving revision 1.3
diff -u -r1.3 x86_64_out.m
--- compiler/x86_64_out.m	8 Feb 2007 01:02:53 -0000	1.3
+++ compiler/x86_64_out.m	19 Feb 2007 06:06:56 -0000
@@ -13,6 +13,11 @@
 % to string writer streams that are attached to the I/O state.
 % (There's no particularly good reason for this latter restriction so
 % it can safely be dropped if necessary.)
+%
+% NOTE:
+% 	The module calls unexpected/2 if there is an instruction which expects
+% 	a different type of operand (For example: an instruction expecting a
+% 	register operand but supplied with an immediate operand type).
 %
 %-----------------------------------------------------------------------------%

@@ -31,9 +36,7 @@
 :- pred output_x86_64_instruction(Stream::in, x86_64_instruction::in,
     io::di, io::uo) is det <= stream.writer(Stream, string, io).

-    % XXX this is misnamed: it should be operand_to_string.
-    %
-:- pred operand_type(operand::in, string::out) is det.
+:- pred operand_to_string(operand::in, string::out) is det.

 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
@@ -41,6 +44,7 @@
 :- implementation.

 :- import_module libs.compiler_util.
+:- import_module ll_backend.x86_64_regs.

 :- import_module bool.
 :- import_module char.
@@ -233,13 +237,10 @@
             Result0 = yes,
             Type0 = yes(Type1)
         ->
-            put(Stream, ",\"" ++ Flags1 ++ "\"", !IO),
-            ( check_pseudo_section_type(Type1) ->
-                put(Stream, "," ++ Type1, !IO)
-            ;
-                unexpected(this_file, "output_x86_64_pseudo_op: section:"
-                    ++ " check_section_type unexpected")
-            )
+            pseudo_section_flags_to_string(Flags1, "", FlagsStr),
+            put(Stream, ",\"" ++ FlagsStr ++ "\"", !IO),
+            check_pseudo_section_type(Type1, Type1Str),
+            put(Stream, "," ++ Type1Str, !IO)
         ;
             unexpected(this_file, "output_x86_64_pseudo_op: section:"
                 ++ " check_section_flags_and_type unexpected")
@@ -291,12 +292,8 @@
 output_x86_64_pseudo_op(Stream, title(Heading), !IO) :-
     put(Stream, "\t.title\t" ++ Heading ++ "\n", !IO).
 output_x86_64_pseudo_op(Stream, x86_64_pseudo_type(Name, Desc), !IO) :-
-    ( check_pseudo_type_desc(Desc) ->
-        put(Stream, "\t.type\t" ++ Name ++ "," ++ Desc ++ "\n", !IO)
-    ;
-       unexpected(this_file, "output_x86_64_pseudo_op: x86_64_pseudo_type:"
-            ++ " unexpected: check_pseudo_type_desc failed")
-    ).
+    check_pseudo_type_desc(Desc, DescType),
+    put(Stream, "\t.type\t" ++ Name ++ "," ++ DescType ++ "\n", !IO).
 output_x86_64_pseudo_op(Stream, uleb128(ExprList), !IO) :-
     output_pseudo_op_str_args(Stream, ".uleb128", ExprList, !IO).
 output_x86_64_pseudo_op(Stream, val(Addr), !IO) :-
@@ -394,74 +391,96 @@
     % Check if the FLAGS and TYPE argumentis of '.section' pseudo-op
     % are valid.
     %
-:- pred check_section_flags_and_type(string::in, maybe(string)::in,
-    bool::out) is det.
+:- pred check_section_flags_and_type(list(pseudo_section_flag)::in,
+    maybe(pseudo_section_type)::in, bool::out) is det.

 check_section_flags_and_type(Flags, Type0, Result) :-
-    (  string.contains_char(Flags, 'M') ->
+    check_pseudo_section_m_flag(Flags, Result0),
+    (
+        Result0 = yes,
         (
             Type0 = yes(Type1),
-            string.length(Type1) > 0
+            check_pseudo_section_type(Type1, _)
         ->
             true
         ;
             unexpected(this_file, "check_section_flags_and_type:"
-               ++ " unexpected: flag")
+               ++ " unexpected: flag 'm' has to have 'type' arguments")
         )
     ;
+        Result0 = no,
         true
     ),
-    string.to_char_list(Flags, CharList),
-    check_pseudo_section_flags(CharList, Result0),
+    check_pseudo_section_flags(Flags, Result1),
     (
-        Result0 = yes,
+        Result1 = yes,
         Result = yes
     ;
-        Result0 = no,
+        Result1 = no,
         Result = no
     ).

     % Check if the FLAGS argument of '.section' pseudo-op is valid.
     %
-:- pred check_pseudo_section_flags(list(char)::in, bool::out) is det.
+:- pred check_pseudo_section_flags(list(pseudo_section_flag)::in, bool::out)
+    is det.

 check_pseudo_section_flags([], yes).
-check_pseudo_section_flags([Char | Chars], Result) :-
-    ( string.contains_char(section_pseudo_op_flags, Char) ->
-        check_pseudo_section_flags(Chars, Result)
-    ;
-        Result = no
-    ).
+check_pseudo_section_flags([Flag | Flags], Result) :-
+    pseudo_section_flag(Flag, _),
+    check_pseudo_section_flags(Flags, Result).

-    % The optional FLAGS argument of '.section' pseudo-op contains any
-    % combination of:
-    % 'a'   - section is allocatable
-    % 'w'   - section is writable
-    % 'x'   - section is executable
-    % 'M'   - section is mergeable
-    % 'S'   - section contains zero terminated string
+    % Returns a string representation of optional flag arguments of
+    % .section pseudo instruction.
     %
-:- func section_pseudo_op_flags = string.
+:- pred pseudo_section_flags_to_string(list(pseudo_section_flag)::in,
+    string::in, string::out) is det.
+
+pseudo_section_flags_to_string([], Result, Result).
+pseudo_section_flags_to_string([Flag | Flags], Result0, Result) :-
+    pseudo_section_flag(Flag, FlagString),
+    pseudo_section_flags_to_string(Flags, FlagString ++ Result0, Result).
+
+    % Returns a string representation of a .section pseudo-instruction flag.
+    %
+:- pred pseudo_section_flag(pseudo_section_flag::in, string::out) is det.
+
+pseudo_section_flag(a, "a").
+pseudo_section_flag(w, "w").
+pseudo_section_flag(x, "x").
+pseudo_section_flag(m, "m").
+pseudo_section_flag(s, "s").

-section_pseudo_op_flags = "awxMS".
+    % If a .section pseudo-instruction has 'm' flag, there has to be
+    % 'type' argument as its second argument.
+    %
+:- pred check_pseudo_section_m_flag(list(pseudo_section_flag)::in,
+    bool::out) is det.
+
+check_pseudo_section_m_flag([], no).
+check_pseudo_section_m_flag([Flag | Flags], Result) :-
+    pseudo_section_flag(Flag, FlagType),
+    ( FlagType = "m" ->
+        Result = yes
+    ;
+        check_pseudo_section_m_flag(Flags, Result)
+    ).

-    % The optional type of '.section' pseudo-op may contain:
-    % @progbits     - section contains data
-    % @nobits       - section does not contain data
+    % Checks if .type argument of .section pseudo-instruction is valid.
     %
-:- pred check_pseudo_section_type(string::in) is semidet.
+:- pred check_pseudo_section_type(pseudo_section_type::in, string::out)
+    is det.

-check_pseudo_section_type("@progbits").
-check_pseudo_section_type("@nobits").
+check_pseudo_section_type(progbits, "@progbits").
+check_pseudo_section_type(nobits, "@nobits").

-    % Two valid values of 'type_desc' field in pseudo-op '.type':
-    %   @function
-    %   @object
+    % Checks if type_desc argument of .section pseudo-instruction is valid.
     %
-:- pred check_pseudo_type_desc(string::in) is semidet.
+:- pred check_pseudo_type_desc(pseudo_section_type_desc::in, string::out)
+    is det.

-check_pseudo_type_desc("@function").
-check_pseudo_type_desc("@function").
+check_pseudo_type_desc(function, "@function").
+check_pseudo_type_desc(object, "@object").

 :- func comment_length = int.

@@ -532,7 +551,7 @@
     check_operand_not_immediate(Src, Result1),
     (
         Result1 = yes,
-        operand_type(Src, SrcType),
+        operand_to_string(Src, SrcType),
         check_operand_register(Dest, DestRes),
         (
             DestRes = yes,
@@ -546,7 +565,7 @@
                     ++ " invalid condition third operand")
             ),
             put(Stream, "\t" ++ Instr ++ "\t", !IO),
-            operand_type(Dest, DestType),
+            operand_to_string(Dest, DestType),
             put(Stream, SrcType ++ ", " ++ DestType ++ "\t", !IO)
         ;
             DestRes = no,
@@ -562,7 +581,7 @@
     check_operand_register(Op, Result),
     (
         Result = yes,
-        operand_type(Op, RegType),
+        operand_to_string(Op, RegType),
         put(Stream, "\tbswap\t" ++ RegType ++ "\t\t", !IO)
     ;
         Result = no,
@@ -581,7 +600,7 @@
     check_operand_not_immediate(Target, Result),
     (
         Result = yes,
-        operand_type(Target, TargetType),
+        operand_to_string(Target, TargetType),
         put(Stream, "\tcall\t" ++ TargetType ++ "\t\t", !IO)
     ;
         Result = no,
@@ -617,8 +636,8 @@
         check_operand_register(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Src, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Src, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\tcmp\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -634,7 +653,7 @@
     check_operand_not_mem_ref(Op, Result),
     (
         Result = no,
-        operand_type(Op, OpType),
+        operand_to_string(Op, OpType),
         put(Stream, "\tcmpxchg8b" ++ OpType, !IO)
     ;
         Result = yes,
@@ -666,23 +685,23 @@
 output_x86_64_inst(Stream, idiv(Operand), !IO) :-
     output_instr_not_imm_dest(Stream, "idiv", Operand, no, !IO).
 output_x86_64_inst(Stream, imul(Src, Dest, Mult), !IO) :-
-    operand_type(Src, SrcType),
+    operand_to_string(Src, SrcType),
     put(Stream, "\timul\t" ++ SrcType, !IO),
     (
         Dest = yes(DestRes),
         check_operand_register(DestRes, Result1),
         (
             Result1 = yes,
-            operand_type(DestRes, DestType)
+            operand_to_string(DestRes, DestType)
         ;
             Result1 = no,
-            TempReg = operand_reg(gp_reg(13)),
-            operand_type(TempReg, DestType)
+            TempReg = operand_reg(get_scratch_reg),
+            operand_to_string(TempReg, DestType)
         ),
         put(Stream, ", " ++ DestType, !IO),
         (
             Mult = yes(MultRes),
-            operand_type(MultRes, Op3),
+            operand_to_string(MultRes, Op3),
             put(Stream, ", " ++ Op3 ++ " ", !IO)
         ;
             Mult = no,
@@ -699,7 +718,7 @@
 output_x86_64_inst(Stream, jrcxz(RelOffset), !IO) :-
     output_instr_8bit_rel_offset(Stream, "jrcxz", RelOffset, !IO).
 output_x86_64_inst(Stream, jmp(Target), !IO) :-
-    operand_type(Target, Op),
+    operand_to_string(Target, Op),
     put(Stream, "\tjmp\t" ++ Op ++ "\t\t", !IO).
 output_x86_64_inst(Stream, lea(Src, Dest), !IO) :-
     check_operand_not_mem_ref(Src, Result1),
@@ -708,8 +727,8 @@
         check_operand_register(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Src, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Src, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\tlea\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -751,7 +770,7 @@
     put(Stream, "\tpopfq\t", !IO).
 output_x86_64_inst(Stream, push(Operand), !IO) :-
     put(Stream, "\tpush\t", !IO),
-    operand_type(Operand, OperandType),
+    operand_to_string(Operand, OperandType),
     put(Stream, OperandType ++ "\t", !IO).
 output_x86_64_inst(Stream, pushfq, !IO) :-
     put(Stream, "\tpushfq\t", !IO).
@@ -762,8 +781,8 @@
         check_operand_not_immediate(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Amnt, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Amnt, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\trc\t" ++ Cond, !IO),
             put(Stream, Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
@@ -807,8 +826,8 @@
         check_operand_not_immediate(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Amnt, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Amnt, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\tro" ++ Dir ++ "\t", !IO),
             put(Stream, Op1 ++ ", " ++ Op2 ++ "\t\t", !IO)
         ;
@@ -828,8 +847,8 @@
         check_operand_not_immediate(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Amnt, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Amnt, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\tsal\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -848,8 +867,8 @@
         check_operand_not_immediate(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Amnt, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Amnt, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\tshl\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -868,8 +887,8 @@
         check_operand_not_immediate(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Amnt, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Amnt, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\tsar\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -903,9 +922,9 @@
             check_operand_register(Reg, Result3),
             (
                 Result3 = yes,
-                operand_type(Amnt, Op1),
-                operand_type(Amnt, Op2),
-                operand_type(Amnt, Op3),
+                operand_to_string(Amnt, Op1),
+                operand_to_string(Amnt, Op2),
+                operand_to_string(Amnt, Op3),
                 put(Stream, "\tshld\t" ++ Op1 ++ ", ", !IO),
                 put(Stream, Op2 ++ ", " ++ Op3 ++ "\t", !IO)
             ;
@@ -930,8 +949,8 @@
         check_operand_not_immediate(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Amnt, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Amnt, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\tshr\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -953,9 +972,9 @@
             check_operand_register(Reg, Result3),
             (
                 Result3 = yes,
-                operand_type(Amnt, Op1),
-                operand_type(Amnt, Op2),
-                operand_type(Amnt, Op3),
+                operand_to_string(Amnt, Op1),
+                operand_to_string(Amnt, Op2),
+                operand_to_string(Amnt, Op3),
                 put(Stream, "\tshrd\t" ++ Op1 ++ ", ", !IO),
                 put(Stream, Op2 ++ ", " ++ Op3 ++ "\t", !IO)
             ;
@@ -986,8 +1005,8 @@
         check_operand_not_immediate(Src2, Result2),
         (
             Result2 = yes,
-            operand_type(Src1, Op1),
-            operand_type(Src2, Op2),
+            operand_to_string(Src1, Op1),
+            operand_to_string(Src2, Op2),
             put(Stream, "\ttest\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -1006,8 +1025,8 @@
         check_operand_not_immediate(Dest, Result2),
         (
             Result2 = yes,
-            operand_type(Src, Op1),
-            operand_type(Dest, Op2),
+            operand_to_string(Src, Op1),
+            operand_to_string(Dest, Op2),
             put(Stream, "\txadd\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -1026,8 +1045,8 @@
         check_operand_reg_or_mem(Src2, Result2),
         (
             Result2 = yes,
-            operand_type(Src1, Op1),
-            operand_type(Src2, Op2),
+            operand_to_string(Src1, Op1),
+            operand_to_string(Src2, Op2),
             put(Stream, "\txchg\t" ++ Op1 ++ ", " ++ Op2 ++ "\t", !IO)
         ;
             Result2 = no,
@@ -1062,26 +1081,46 @@

     % Output a string representation of an immediate value.
     %
-:- pred imm_op_type(imm_operand::in, string::out) is det.
+:- pred imm_op_to_string(imm_operand::in, string::out) is det.

-imm_op_type(imm8(int8(Val)), ImmVal) :-
+imm_op_to_string(imm8(int8(Val)), ImmVal) :-
     ImmVal = "$" ++ string.int_to_string(Val).
-imm_op_type(imm16(int16(Val)), ImmVal) :-
+imm_op_to_string(imm16(int16(Val)), ImmVal) :-
     ImmVal = "$" ++ string.int_to_string(Val).
-imm_op_type(imm32(int32(Val)), ImmVal) :-
+imm_op_to_string(imm32(int32(Val)), ImmVal) :-
     ImmVal = "$" ++ string.int_to_string(Val).

-:- func reg_type(gp_reg) = string.
+:- pred reg_to_string(x86_64_reg::in, string::out) is det.

-reg_type(gp_reg(RegNum)) = "%r" ++ string.int_to_string(RegNum).
+reg_to_string(rax, "%rax").
+reg_to_string(rbx, "%rbx").
+reg_to_string(rcx, "%rcx").
+reg_to_string(rdx, "%rdx").
+reg_to_string(rbp, "%rbp").
+reg_to_string(rsi, "%rsi").
+reg_to_string(rdi, "%rdi").
+reg_to_string(rsp, "%rsp").
+reg_to_string(r8, "%r8").
+reg_to_string(r9, "%r9").
+reg_to_string(r10, "%r10").
+reg_to_string(r11, "%11").
+reg_to_string(r12(Offset), RegStr) :-
+    ( Offset > 0 ->
+        RegStr = string.int_to_string(Offset) ++ "(%r12)"
+    ;
+        RegStr = "%r12"
+    ).
+reg_to_string(r13, "%r13").
+reg_to_string(r14, "%r14").
+reg_to_string(r15, "%r15").

     % Output a string representation of a memory reference.
     %
-:- pred mem_ref_type(x86_64_mem_ref::in, string::out) is det.
+:- pred mem_ref_to_string(x86_64_mem_ref::in, string::out) is det.

-mem_ref_type(mem_abs(DirectMemRef), MemRefVal) :-
+mem_ref_to_string(mem_abs(DirectMemRef), MemRefVal) :-
     base_address_type(DirectMemRef, MemRefVal).
-mem_ref_type(mem_rip(InstrPtr), MemRefVal) :-
+mem_ref_to_string(mem_rip(InstrPtr), MemRefVal) :-
     instr_ptr_type(InstrPtr, MemRefVal).

     % Output a string representation of a base address in a memory reference.
@@ -1089,11 +1128,12 @@
 :- pred base_address_type(base_address::in, string::out) is det.

 base_address_type(base_reg(Offset, Reg), BaseAddress) :-
+    reg_to_string(Reg, RegStr),
     ( Offset = 0 ->
-        BaseAddress = "(" ++ reg_type(Reg) ++ ")"
+        BaseAddress = "(" ++ RegStr ++ ")"
     ;
         BaseAddress = string.int_to_string(Offset) ++
-            "(" ++ reg_type(Reg) ++ ")"
+            "(" ++ RegStr ++ ")"
     ).
 base_address_type(base_expr(Expr), DispType) :-
     DispType = "$" ++ Expr.
@@ -1117,9 +1157,9 @@

     % Output a string representation of a relative offset.
     %
-:- pred rel_offset_type(rel_offset::in, string::out) is det.
+:- pred rel_offset_to_string(rel_offset::in, string::out) is det.

-rel_offset_type(ro8(int8(Val)), RelOffsetVal) :-
+rel_offset_to_string(ro8(int8(Val)), RelOffsetVal) :-
     check_signed_int_size(8, Val, Result),
     (
         Result = yes,
@@ -1130,10 +1170,10 @@
         )
     ;
         Result = no,
-        unexpected(this_file, "rel_offset_type: ro8(int8): unexpected:"
+        unexpected(this_file, "rel_offset_to_string: ro8(int8): unexpected:"
             ++ " check_signed_int_size failed")
     ).
-rel_offset_type(ro16(int16(Val)), RelOffsetVal) :-
+rel_offset_to_string(ro16(int16(Val)), RelOffsetVal) :-
     check_signed_int_size(16, Val, Result),
     (
         Result = yes,
@@ -1144,10 +1184,10 @@
         )
     ;
         Result = no,
-        unexpected(this_file, "rel_offset_type: ro16(int16): unexpected"
+        unexpected(this_file, "rel_offset_to_string: ro16(int16): unexpected"
             ++ " check_signed_int_size failed")
     ).
-rel_offset_type(ro32(int32(Val)), RelOffsetVal) :-
+rel_offset_to_string(ro32(int32(Val)), RelOffsetVal) :-
     check_signed_int_size(32, Val, Result),
     (
         Result = yes,
@@ -1158,20 +1198,20 @@
         )
     ;
         Result = no,
-        unexpected(this_file, "rel_offset_type: ro32(int32): unexpected"
+        unexpected(this_file, "rel_offset_to_string: ro32(int32): unexpected"
             ++ " check_signed_int_size failed")
     ).


-operand_type(operand_reg(Reg), RegType) :-
-    RegType = reg_type(Reg).
-operand_type(operand_imm(Imm), ImmVal) :-
-    imm_op_type(Imm, ImmVal).
-operand_type(operand_mem_ref(MemRef), MemRefVal) :-
-    mem_ref_type(MemRef, MemRefVal).
-operand_type(operand_rel_offset(RelOffset), RelOffsetType) :-
-    rel_offset_type(RelOffset, RelOffsetType).
-operand_type(operand_label(Label), (Label)).
+operand_to_string(operand_reg(Reg), RegType) :-
+    reg_to_string(Reg, RegType).
+operand_to_string(operand_imm(Imm), ImmVal) :-
+    imm_op_to_string(Imm, ImmVal).
+operand_to_string(operand_mem_ref(MemRef), MemRefVal) :-
+    mem_ref_to_string(MemRef, MemRefVal).
+operand_to_string(operand_rel_offset(RelOffset), RelOffsetType) :-
+    rel_offset_to_string(RelOffset, RelOffsetType).
+operand_to_string(operand_label(Label), (Label)).

 %-----------------------------------------------------------------------------%
 %
@@ -1186,13 +1226,13 @@
     is det <= stream.writer(Stream, string, io).

 output_instr_not_imm_dest(Stream, Instr, Op1, Op2, !IO) :-
-    operand_type(Op1, Op1Type),
+    operand_to_string(Op1, Op1Type),
     (
         Op2 = yes(Op2Result),
         check_not_both_memory_ops(Op1, Op2Result, Result1),
         (
             Result1 = yes,
-            operand_type(Op2Result, Op2Type),
+            operand_to_string(Op2Result, Op2Type),
             check_operand_not_immediate(Op2Result, Result2),
             (
                 Result2 = yes,
@@ -1225,7 +1265,7 @@
    check_operand_rel_offset(RelOffset, Result1),
    (
         Result1 = yes,
-        operand_type(RelOffset, RelOffsetType),
+        operand_to_string(RelOffset, RelOffsetType),
         ( string.to_int(RelOffsetType, Val) ->
             check_signed_int_size(8, Val, Result2),
             (
@@ -1255,11 +1295,11 @@
     check_operand_not_immediate(Src, Result1),
     (
         Result1 = yes,
-        operand_type(Src, Op1),
+        operand_to_string(Src, Op1),
         check_operand_not_mem_ref(Idx, Result2),
         (
             Result2 = yes,
-            operand_type(Idx, Op2),
+            operand_to_string(Idx, Op2),
             ( string.to_int(Op2, IdxInt) ->
                 check_signed_int_size(8, IdxInt, Result3),
                 (
@@ -1297,14 +1337,14 @@
         instr_condition(Cond, CondRes),
         put(Stream, "\t" ++ Instr, !IO),
         put(Stream, CondRes ++ "\t", !IO),
-        operand_type(Op1, Op1Type),
+        operand_to_string(Op1, Op1Type),
         put(Stream, Op1Type, !IO),
         (
             Op2 = yes(Op2Res),
             check_operand_register(Op2Res, Result3),
             (
                 Result3 = yes,
-                operand_type(Op2Res, Op2Type),
+                operand_to_string(Op2Res, Op2Type),
                 put(Stream, ", " ++ Op2Type, !IO)
             ;
                 Result3 = no,
@@ -1327,7 +1367,7 @@

 check_rc_first_operand(Op, Result) :-
     ( Op = operand_imm(_) ->
-        operand_type(Op, OpType),
+        operand_to_string(Op, OpType),
         ( string.to_int(OpType, OpInt) ->
             check_unsigned_int_size(8, OpInt, Result1),
             (
@@ -1393,7 +1433,7 @@

 check_operand_unsigned_imm_or_reg(Operand, Result) :-
     ( Operand = operand_imm(Imm) ->
-        imm_op_type(Imm, ImmType),
+        imm_op_to_string(Imm, ImmType),
         ( string.to_int(ImmType, ImmInt) ->
             (
                 check_unsigned_int_size(32, ImmInt, Result1),
Index: compiler/x86_64_regs.m
===================================================================
RCS file: compiler/x86_64_regs.m
diff -N compiler/x86_64_regs.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ compiler/x86_64_regs.m	19 Feb 2007 06:05:32 -0000
@@ -0,0 +1,258 @@
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2007 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: x86_64_regs.m.
+% Main author: fhandoko.
+%
+% llds.lval ===> reg_locn
+%
+% NOTE:
+%   The first field of reg_map type is a list of available scratch registers.
+%   Although the reg_map is updated everytime a scratch register is used in an
+%   instruction (updated here means that the new reg_map is equivalent to the
+%   old reg_map with the first element (the used scratch register) being
+%   removed), it does not seem to work well. It seems to always get the first
+%   scratch register. So, there are instructions in which the next instruction
+%   overrides the value in a scratch register being used by a previous
+%   instruction.
+%-----------------------------------------------------------------------------%
+
+:- module ll_backend.x86_64_regs.
+:- interface.
+
+:- import_module ll_backend.llds.
+:- import_module ll_backend.x86_64_instrs.
+
+:- import_module assoc_list.
+
+%----------------------------------------------------------------------------%
+
+    % This type stores information about the mapping from MVM registers
+    % to x86_64 registers.  MVM registers will correspond to either
+    % (1) a physical x86_64 register
+    % or (2) a slot in the fake_reg array (see runtime/mercury_engine.h).
+    %
+:- type reg_map.
+
+    % This type represents the location of an MVM register on x86_64
+    % hardware.  `actual/1' is a phyical x86_64 register.  `virtual/1'
+    % is the slot in the fake reg array given by the argument.
+    %
+:- type reg_locn
+	--->	actual(x86_64_reg)
+	;		virtual(int).		% Index into fake reg array.
+
+    % Create an association list of lvals and reg_lcons.
+    %
+:- pred llds_reg_locn(assoc_list(llds.lval, reg_locn)::out) is det.
+
+    % Create a reg_map given an association list of lvals and reg_locns.
+    % Throws an exception if an l-value in the association list does not
+    % correspond to a MVM register.
+    %
+:- func reg_map_init(assoc_list(llds.lval, reg_locn)) = reg_map.
+
+    % Given an LLDS lval that corresponds to a virtual machine register
+    % look up it's actual location according to the current register mapping.
+    % Throws an exception for l-values that do not correspond to virtual
+    % machine registers.
+    %
+:- func reg_map_lookup_reg_locn(reg_map, llds.lval) = reg_locn.
+
+    % Reset the contents of scratch registers. As a result, all scratch
+    % registers will be available.
+    %
+:- pred reg_map_reset_scratch_reg_info(reg_map::in, reg_map::out) is det.
+
+    % Given a reg_map, get the first available scratch register.
+    %
+:- func reg_map_get_scratch_reg(reg_map) = x86_64_reg.
+
+    % Get an x86_64_register.
+    %
+:- func get_scratch_reg = x86_64_reg.
+
+    % Remove the first index of scratch register list (which is the first
+    % field of reg_map).
+    %
+:- pred reg_map_remove_scratch_reg(reg_map::in, reg_map::out) is det.
+
+%----------------------------------------------------------------------------%
+%----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module libs.compiler_util.
+
+:- import_module bool.
+:- import_module list.
+:- import_module map.
+:- import_module pair.
+:- import_module string.
+
+:- import_module io.
+
+%----------------------------------------------------------------------------%
+%
+% LLDS -> x86_64 register mapping.
+%
+
+:- type reg_map
+	--->	reg_map(
+				scratch_reg_info        :: list(x86_64_reg),
+                % A list of available scratch registers.
+				map(llds.lval, reg_locn)
+			    % Mapping lval to an actual or virtual register.
+            ).
+
+%----------------------------------------------------------------------------%
+
+llds_reg_locn([
+        sp              - actual(r12(0)),       % The top of non-det stack frame.
+        succip          - actual(r13),
+        reg(reg_r, 1)   - actual(r14),
+        reg(reg_r, 2)   - actual(r15),
+        reg(reg_r, 3)   - virtual(4),
+        hp              - virtual(5),
+        reg(reg_r, 4)   - virtual(6),
+        reg(reg_r, 5)   - virtual(7),
+        curfr           - virtual(8),
+        maxfr           - virtual(9),
+        reg(reg_r, 6)   - virtual(10),
+        reg(reg_r, 7)   - virtual(11),
+        reg(reg_r, 8)   - virtual(12),
+        reg(reg_r, 9)   - virtual(13),
+        reg(reg_r, 10)  - virtual(14),
+        reg(reg_r, 11)  - virtual(15),
+        reg(reg_r, 12)  - virtual(16),
+        reg(reg_r, 13)  - virtual(17),
+        reg(reg_r, 14)  - virtual(18),
+        reg(reg_r, 15)  - virtual(19),
+        reg(reg_r, 16)  - virtual(20),
+        reg(reg_r, 17)  - virtual(21),
+        reg(reg_r, 18)  - virtual(22),
+        reg(reg_r, 19)  - virtual(23),
+        reg(reg_r, 20)  - virtual(24),
+        reg(reg_r, 21)  - virtual(25),
+        reg(reg_r, 22)  - virtual(26),
+        reg(reg_r, 23)  - virtual(27),
+        reg(reg_r, 24)  - virtual(28),
+        reg(reg_r, 25)  - virtual(29),
+        reg(reg_r, 26)  - virtual(30),
+        reg(reg_r, 27)  - virtual(31),
+        reg(reg_r, 28)  - virtual(32),
+        reg(reg_r, 29)  - virtual(33),
+        reg(reg_r, 30)  - virtual(34),
+        reg(reg_r, 31)  - virtual(35),
+        reg(reg_r, 32)  - virtual(36)
+    ]).
+
+reg_map_init(AssocRegMap) = RegMap :-
+    map.init(Map0),
+    assoc_list.keys(AssocRegMap, ListOfKeys),
+    check_if_all_mvm_registers(ListOfKeys, Result),
+    (
+        Result = yes,
+        map.det_insert_from_assoc_list(Map0, AssocRegMap, Map1),
+        RegMap = reg_map(init_scratch_regs, Map1)
+    ;
+        Result = no,
+        unexpected(this_file, "reg_map_init: unexpected: non-MVM register"
+            ++ " found in the association list")
+    ).
+
+reg_map_lookup_reg_locn(Map, Lval) = RegLocn :-
+    Map = reg_map(_, RegMap),
+    (
+        ( Lval = parent_stackvar(_)
+        ; Lval = succip_slot(_)
+        ; Lval = redoip_slot(_)
+        ; Lval = redofr_slot(_)
+        ; Lval = succfr_slot(_)
+        ; Lval = prevfr_slot(_)
+        ; Lval = mem_ref(_)
+        ; Lval = global_var_ref(_)
+        )
+    ->
+        unexpected(this_file, "reg_map_lookup_reg_locn: unexpected: "
+            ++ "lval is not a virtual machine register")
+    ;
+        Lval = lvar(_)
+    ->
+        unexpected(this_file, "reg_map_lookup_reg_locn: unexpected: "
+            ++ "lvar/1 during x86_64 code generation")
+    ;
+        map.lookup(RegMap, Lval, RegLocn)
+    ).
+
+reg_map_reset_scratch_reg_info(RegMap0, RegMap) :-
+    ScratchRegs = init_scratch_regs,
+    RegMap = RegMap0 ^ scratch_reg_info := ScratchRegs.
+
+reg_map_get_scratch_reg(RegMap) = ScratchReg :-
+    ScratchRegs = RegMap ^ scratch_reg_info,
+    ( list.index0(ScratchRegs, first_list_idx, ScratchReg0) ->
+        ScratchReg = ScratchReg0
+    ;
+        unexpected(this_file, "reg_map_get_scratch_reg: unexpected:"
+           ++ " scratch registers exhausted")
+    ).
+
+reg_map_remove_scratch_reg(RegMap0, RegMap) :-
+    ScratchRegs0 = RegMap0 ^ scratch_reg_info,
+    ( list.drop(first_list_idx, ScratchRegs0, ScratchRegs1) ->
+        RegMap = RegMap0 ^ scratch_reg_info := ScratchRegs1
+    ;
+        unexpected(this_file, "reg_map_remove_scratch_reg: unexpected:"
+           ++ " scratch registers exhausted")
+    ).
+
+    % Check if all l-values in the association list correspond to MVM
+    % registers.
+    %
+:- pred check_if_all_mvm_registers(list(llds.lval)::in, bool::out) is det.
+
+check_if_all_mvm_registers([], yes).
+check_if_all_mvm_registers([Lval | Lvals], Result) :-
+    (
+        ( Lval = reg(reg_r, _)
+        ; Lval = succip
+        ; Lval = maxfr
+        ; Lval = curfr
+        ; Lval = hp
+        ; Lval = sp
+        )
+    ->
+        check_if_all_mvm_registers(Lvals, Result)
+    ;
+        Result = no
+    ).
+
+    % Initialize scratch registers to be used in an instruction.
+    %
+:- func init_scratch_regs = list(x86_64_reg).
+
+init_scratch_regs = [r9, r10, r11].
+
+get_scratch_reg = r9.
+
+    % Returns the index of the first element in the list.
+    %
+:- func first_list_idx = int.
+
+first_list_idx = 0.
+
+%----------------------------------------------------------------------------%
+
+:- func this_file = string.
+
+this_file = "x86_64_regs.m".
+
+%----------------------------------------------------------------------------%
+:- end_module ll_backend.x86_64_regs.
+%----------------------------------------------------------------------------%
2007-02-27 20:36:29 +00:00

1347 lines
48 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2007 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: x86_64_instrs.m.
% Main author: fhandoko.
%
% This module contains the representations of the x86_64 instructions.
%
% NOTE:
% Instructions that make use of segment registers and string operations
% (such as compare_strings) have not been implemented.
%-----------------------------------------------------------------------------%
:- module ll_backend.x86_64_instrs.
:- interface.
:- import_module hlds.hlds_pred.
:- import_module hlds.code_model.
:- import_module ll_backend.llds.
:- import_module mdbcomp.prim_data.
:- import_module counter.
:- import_module list.
:- import_module maybe.
:- import_module set.
%-----------------------------------------------------------------------------%
% We turn a llds.c_file into this.
%
:- type x86_64_module
---> x86_64_module(
x86_64_modulename :: module_name,
% The name of this x86_64 module.
x86_64_procs :: list(list(x86_64_procedure))
% The code.
).
:- func init_x86_64_module(module_name) = x86_64_module.
:- func init_x86_64_proc(c_procedure) = x86_64_procedure.
:- func init_x86_64_instruction = x86_64_instruction.
%-----------------------------------------------------------------------------%
% We turn an llds.c_procedure into one of these.
% XXX Do we really need to replicate all these fields from the llds?
%
:- type x86_64_procedure
---> x86_64_procedure(
x86_64_name :: string,
% Predicate name.
x86_64_arity :: int,
% Original arity.
x86_64_id :: pred_proc_id,
% The pred_proc_id of this code.
x86_64_code_model :: code_model,
% The code model of the procedure.
x86_64_code :: list(x86_64_instruction),
% The code for this procedure.
x86_64_proc_label :: proc_label,
% Proc_label of this procedure.
x86_64_label_nums :: counter,
x86_64_may_alter_rtti :: may_alter_rtti,
% The compiler is allowed to perform
% optimizations on this procedure
% that could alter RTTI information
% (e.g. the set of variables live at
% a label) only if this field is set
% to `may_alter_rtti'.
x86_64_c_global_vars :: set(string)
).
%-----------------------------------------------------------------------------%
:- type x86_64_instruction
---> x86_64_instr(
x86_64_inst :: list(x86_64_instr),
x86_64_inst_comment :: string
).
:- type label_name == string.
:- type x86_64_instr
---> x86_64_comment(string)
; x86_64_label(label_name)
; x86_64_directive(pseudo_op)
; x86_64_instr(x86_64_inst).
%-----------------------------------------------------------------------------%
% Signed integers of various sizes.
%
:- type int8 ---> int8(int). % In bottom 8 bits.
:- type int16 ---> int16(int). % In bottom 16 bits.
:- type int32 ---> int32(int). % in bottom 32 bits.
% Unsigned integers of various sizes.
%
:- type uint8 ---> uint8(int). % In bottom 8 bits.
:- type uint16 ---> uint16(int). % In bottom 16 bits.
:- type uint32 ---> uint32(int). % In bottom 32 bits.
%-----------------------------------------------------------------------------%
% Conditional direction.
%
:- type direction
---> f % Forward.
; r. % Reverse.
% Condition of flags register.
%
:- type condition
---> o % Overflow (OF = 1).
; no % Not Overflow (OF = 0).
; b % Below (CF = 1).
; c % Carry (CF = 1).
; nae % Not Above or Equal (CF = 1).
; nb % Not Below (CF = 0).
; nc % Not Carry (CF = 0).
; ae % Above or Equal (CF = 0).
; z % Zero (ZF = 1).
; e % Equal (ZF = 1).
; nz % Not Zero (ZF = 0).
; ne % Not Equal (ZF = 0).
; be % Below or Equal (CF = 1 or ZF = 1).
; na % Not Above (CF = 1 or ZF = 1).
; nbe % Not Below or Equal (CF = 0 or ZF = 0).
; a % Above (CF = 0 or ZF = 0).
; s % Sign (SF = 1).
; ns % Not Sign (SF = 0).
; p % Parity (PF = 1).
; pe % Parity even (PF = 1).
; np % Not parity (PF = 0).
; po % Parity odd (PF = 0).
; l % Less (SF <> OF).
; nge % Not Greater or Equal (SF <> OF).
; nl % Not Less (SF = OF).
; ge % Greater or Equal (SF = OF).
; le % Less or Equal (ZF = 1 or SF <> OF).
; ng % Not Greater (ZF = 1 or SF <> OF).
; nle % Not Less or Equal (ZF = 0 and SF = OF).
; g. % Greater (ZF = 0 and SF = OF).
% Optional flags argument of .section pseudo_op.
%
:- type pseudo_section_flag
---> a % section is allocatable.
; w % section is writable.
; x % section is executable.
; m % section is mergeable.
; s. % section contains zero terminated string.
% An optional type of '.section' pseudo-op.
%
:- type pseudo_section_type
---> progbits % section contains data.
; nobits. % section does not contain data.
% type_desc field of .section pseudo-op.
%
:- type pseudo_section_type_desc
---> function
; object.
%-----------------------------------------------------------------------------%
%
% x86_64 pseudo-ops.
%
% GNU assembler pseudo-ops for the x86_64.
% Details on GNU assembler 'as' documentation in the section entitled
% "Pseudo Ops".
%
:- type pseudo_op
---> abort
% stop the assembly immediately.
; align(
align_bits :: int,
align_fill_value :: maybe(int),
align_skip_bytes :: maybe(int)
)
% Advance the location counter until it is a multiple of
% 'align_bits'. The second and third arguments are optional.
% 'align_fill_value': value to be stored in the padding bytes.
% 'align_skip_bytes': maximum number of bytes to be skipped.
; ascii(
ascii_literals :: list(string)
)
% 'ascii_literals' contain zero or more string literals. Assembles
% each literal into consecutive addresses.
; asciiz(
asciiz_literals :: list(string)
)
% Simiar to '.ascii', but each string is followed by a zero byte.
; balign(
balign_bits :: int,
balign_fill_value :: maybe(int),
balign_skip_bytes :: maybe(int)
)
% Similar to 'align'.
; byte(
byte_exprs :: list(string)
)
% 'byte_exprs' contains zero or more expressions. Each expression is
% assembled into the next byte.
; comm(
comm_symbol :: string,
comm_length :: int,
comm_align :: maybe(int)
)
% Declare a common symbol 'comm_symbol'. Allocate 'comm_length'
% bytes if the symbol has not been defined.
% (optional) 'comm_align' is the desired alignment of the symbol.
; data(
data_subsection :: maybe(int)
)
% Tells assembler to assemble the following statements onto the end
% of the data subsection numbered 'data_subsection'.
; desc(
desc_symbol :: string,
desc_abs_expr :: string
)
% Set the descriptor of the symbol to the low 16 bits of
% 'desc_abs_expr'.
; def(
def_name :: string
)
% Begin defining debugging information for a symbol 'def_name'.
; dim
% Tell to include auxiliary debugging information in the symbol
% table.
; double(
double_nums :: list(float)
)
% 'double_nums' contains zero or more floating point numbers.
; eject
% Force a page break.
; x86_64_pseudo_else
% As in 'if-then-else' conditional expression.
; elseif
% Shorthand for the beginning a new '.if' block.
; end
% Mark the end of the assembly file.
; endef
% End of a symbol definition begun with '.def'.
; endfunc
% End of a function specified with '.func'.
; endif
% End of a block of conditional code.
; endm
% End of assembly macro.
; equ(
equ_symbol :: string,
equ_expr :: string
)
% Set the value of 'equ_symbol' to 'equ_expr'.
; equiv(
equiv_symbol :: string,
equiv_expr :: string
)
% Like '.equ' except that the assembler will signal an error
% if 'equiv_symbol' is already defined.
; err
% Print an error message.
; exitm
% Exit early from the current macro definition.
; extern
% Ignored. '.extern' is accepted for compatibility only.
; fail_(
fail_expr :: string
)
% Generate an error or a warning. If 'fail_expr' is 500 or more, it
% prints a warning message. Otherwise, it prints an error message.
; file(
file_name :: string
)
% Start a new logical file.
; fill(
fill_repeat :: int,
fill_size :: maybe(int),
fill_value :: maybe(int)
)
% Emits 'fill_repeat' copies of 'fill_size' byte. The contents of
% each 'fil_repeat' bytes is taken from an 8-byte number. The
% highest order 4 bytes are zero. The lowest order 4 bytes are
% 'fill_value'. The last 2 arguments are optional.
; float(
float_nums :: list(float)
)
% 'float_nums' contains zero or more floating point numbers.
; func_(
func_name :: string,
func_label :: string
)
% Emits debugging information to denote function 'func_name' and
% is ignored unless the file is assembled with debugging enabled.
% 'func_label' is the entry point of the function.
; global(
global_symbol :: string
)
% makes the global_symbol' visible to other programs that are linked
% with it.
; globl(
globl_symbol :: string
)
% makes the globl_symbol' visible to other programs that are linked
; hidden(
hidden_name :: string
)
% Override the named symbol default visibility (which is set by
% their binding: local, global or weak).
; hword(
hword_exprs :: list(string)
)
% 'hword_exprs' contains zero or more expressions and emit a 16-bit
% number for each. Synonym for '.short'.
; ident
% To place tags in object files.
; x86_64_pseudo_if(
if_expr :: string
)
% Mark the beginning of a conditional section.
; ifdef(
ifdef_symbol :: string
)
% Assemble the following section of code if 'ifdef_symbol' has been
% defined.
; ifc(
ifc_string1 :: string,
ifc_string2 :: string
)
% Assemble the following section of code if the two strings are the
% same.
; ifeq(
ifeq_expr :: string
)
% Assemble the following section of code if the argument is zero.
; ifge(
ifge_expr :: string
)
% Assemble the following section of code if the argument is greater
% than equal to zero.
; ifgt(
ifgt_expr :: string
)
% Assemble the following section of code if the argument is greater
% than zero.
; ifle(
ifle_expr :: string
)
% Assemble the following section of code if the argument is less
% than equal to zero.
; iflt(
iflt_expr :: string
)
% Assemble the following section of code if the argument is less
% than zero.
; ifnc(
ifnc_string1 :: string,
ifnc_string2 :: string
)
% Assemble the following section of code if the two strings are not
% the same.
; ifndef(
ifndef_symbol :: string
)
% Assemble the following section of code if 'ifndef_symbol' has not
% been defined.
; ifnotdef(
ifnotdef_symbol :: string
)
% Synonym for ifndef.
; ifne(
ifne_expr :: string
)
% Assemble the following section of code if the argument is not
% equal to zero.
; ifnes(
ifnes_string1 :: string,
ifnes_string2 :: string
)
% Assemble the following section of code if the two strings are not
% the same.
; include(
include_file :: string
)
% Include supporting 'include_file'.
; int(
int_exprs :: list(string)
)
% 'int_exprs' contains zero or more expressions. For each
% expression, emit a number that, at run time, is the value of that
% expression.
; internal(
internal_name :: string
)
% Like '.hidden' pseudo-op.
; lcomm(
lcomm_symbol :: string,
lcomm_length :: int
)
% Reserve 'lcomm_length' bytes for a local common denoted by
% 'lcomm_symbol'.
; line(
line_number :: int
)
% Change the logical line number.
; list
% Control whether or not assembly listings are generated. This
% increments the internal counter.
; long(
long_exprs :: list(string)
)
% Same as '.int'.
; macro
% Define macro to generate assembly output.
; nolist
% Control whether or not assembly listings are generated. It
% decrements an internal counter.
; p2align(
p2align_pow_bit :: int,
p2align_fill_value :: maybe(int),
p2align_skip_bytes :: maybe(int)
)
% Advances the location counter until it is a multiple of
% 2 ^ ('p2align_pow_bit')
% p2align_fill_value: value to be stored in padding bytes.
% 'p2align_skip_bytes: maximum bytes to be skipped.
; popsection
% Replace the current section (and subsection) with the top section
% (and subsection) on the section stack.
; previous
% Swap the current section (and subsection) with most recently
% referenced secction (and subsection) prior to this one.
; print(string)
% print string on the standard output.
; protected(
protected_name :: string
)
% Like '.hidden' or '.internal'
; psize(
psize_lines :: int,
psize_cols :: maybe(int)
)
% Declare the number of lines specified by 'psize_lines' and
% an optional number of columns specified by 'psize_cols'.
; purgem(
purgem_name :: string
)
% Undefine the macro 'purgem_name' so that later uses of the string
% will not be expanded.
; pushsection(
pushsection_name :: string,
pushsection_subsect :: int
)
% Push the current section (and subsection) onto the top of the
% section stack and then replace the current section and subsection
% with 'pushsection_name' and 'pushsection_subsect'.
; quad(
quad_bignums :: list(string)
)
% 'quad_bignums' contains zero or more bignums. For each bignum, it
% emits an 8-byte-integer.
; rept(
rept_count :: int
)
% Repeat the sequence of lines between the '.rept' directive and the
% next '.endr' directive 'rept_count' times.
; sbttl(
sbttl_subheading :: string
)
% Use 'subttl_subheading' as the title (immediately after the title
% line).
; scl(
scl_class :: string
)
% Set the storage-class value for a symbol.
; section(
section_name :: string,
section_flags :: maybe(list(pseudo_section_flag)),
section_type :: maybe(pseudo_section_type),
section_entsize :: maybe(int)
)
% ELF section stack manipulation directive.
; set(
set_symbol :: string,
set_expression :: string
)
% Set the value of 'set_symbol' to 'set_expression'
; short(
short_exprs :: list(string)
)
; single(
single_nums :: list(float)
)
% Has the same effect as '.float'.
; size(
size_name :: string,
size_exprs :: string
)
% Set the size associated with symbol'size_name'. The size in bytes
% is computed from 'size_expr'.
; skip(
skip_size :: int,
skip_value :: maybe(int)
)
% Emit 'skip_size' bytes, each of value 'skip_value' (optional).
; sleb128(
sleb128_exprs :: list(string)
)
% Stand for "signed little endian base 128". It is a variable length
% representation of numbers used by the DWARF symbolic.
; space(
space_size :: int,
space_fill :: maybe(int)
)
% Emit 'space_size' bytes, each of value 'space_fill' (optional).
; string(
string_str :: list(string)
)
% Copy the characters in each string inside 'string_strs' to the
% object file.
; struct(
struct_expr :: string
)
% Switch to the absolute section and set the section offset to
% 'struct_expr'.
; subsection(
subsection_name :: string
)
% Replace the current subsection with 'subsection_name'.
; symver(
symver_name :: string,
symver_alias :: string
)
% If 'symver_name' is defined within the file being assembled, it
% creates a symbol alias with the name 'symver_alias'
; tag(
tag_struct_name :: string
)
% Include auxiliary debugging information in the symbol table.
; text(
text_subsection :: maybe(int)
)
% Assemble the following statements onto the end of the text
% subsection numbered zero or 'text_subsection' (if present).
; title(
title_heading :: string
)
% Use 'title_heading' as the title when generating assembly listing.
; x86_64_pseudo_type(
type_name :: string,
type_desc :: pseudo_section_type_desc
)
% Set the type of symbol'type_name' to be either a function or an
% object symbol.
; uleb128(
uleb128_exprs :: list(string)
)
% Stands for "unsigned little endian base 128". It is a variable
% length representation of numbers used by the DWARF symbolic
% debugging format.
; val(
val_addr :: string
)
% Record 'val_addr' as the value attribute of a symbol table entry.
; version(
version_note :: string
)
% Create a '.note' section and places into it an ELF formatted note
% of type NT_VERSION>
; weak(
weak_names :: list(string)
)
% Set the weak attribute on the list of symbol in 'weak_names'. If
% the symbol has not existed, it will be created.
; word(
word_exprs :: list(string)
).
% 'word_exprs' contains zero or more expressions.
%-----------------------------------------------------------------------------%
%
% x86_64 registers.
%
% 64-bit RFLAGS register holding various status flags and one control flag.
% Details on amd64-prog-man-vol1 manual p38.
%
:- type flags_reg
---> rflags(int).
% General purpose registers on the x86_64.
% Details on amd64-prog-man-vol1 manual p27.
%
:- type offset == int.
:- type x86_64_reg
---> rax
; rbx
; rcx
; rdx
; rbp
; rsi
; rdi
; rsp
; r8
; r9
; r10
; r11
; r12
; r13
; r14
; r15.
% 64-bit instruction pointer register on the x86_64. Instruction pointer
% RIP is used as a base register for relative addressing. x86_64
% instructions using RIP can be:
% rip_constant(%rip)
% rip_expr(%rip)
% Details on GNU assembler 'as' documentation in the section entitled
% "Machine Dependencies: i386-Dependent: i386-Memory".
%
:- type instr_ptr
---> rip_constant(
rip_byte_offset :: int32
)
; rip_expr(
rip_expr :: string
).
%-----------------------------------------------------------------------------%
%
% Operands for x86_64 instructions.
%
% Immediate operands for the x86_64 instruction.
%
:- type imm_operand
---> imm8(int8)
; imm16(int16)
; imm32(int32).
% Memory reference operands for the x86_64 instruction can be an absolute
% address (given as a displacement from the base address of a data segment)
% or an instruction-relative address.
% Details on amd64-prog-man-vol1 p19.
%
:- type x86_64_mem_ref
---> mem_abs(
mem_abs_address :: base_address
)
; mem_rip(
instr_rel_address :: instr_ptr
).
% Base address can be relative to the content of a general-purpose register
% or relative value of an expression (such as arithmetic expression
% containing labels in data segment).
%
:- type base_address
---> base_reg(
base_offset :: int,
base_reg :: x86_64_reg
)
; base_expr(
base_expr :: string
).
% All operands for the x86_64 instructions.
%
:- type operand
---> operand_reg(x86_64_reg)
; operand_imm(imm_operand)
; operand_mem_ref(x86_64_mem_ref)
; operand_rel_offset(rel_offset)
; operand_label(string).
%
% Subtypes of the operand type.
% XXX maybe we should use inst-subtyping for these?
%
% x86_64 instruction's operand is an offset relative to the instruction
% pointer.
%
:- type rel_offset
---> ro8(int8) % Signed 8-bit offset (in bottom 8 bits).
; ro16(int16) % Signed 16-bit offset (in bottom 16 bits).
; ro32(int32). % Signed 32-bit offset (in bottom 32 bits).
% x86_64 instruction's operand can be a register, memory reference,
% signed relative offset or a label.
%
:- type rmrol
---> rmrol_reg(x86_64_reg)
; rmrol_mem_ref(x86_64_mem_ref)
; rmrol_rel_offset(rel_offset)
; rmrol_label(
rmrol_label_name :: string
).
%-----------------------------------------------------------------------------%
%
% x86_64 instructions.
%
% We use At&T style instructions where the source comes before the
% destination.
%
:- type x86_64_inst
---> adc(
adc_src :: operand,
% immediate, register or memory location
adc_dst :: operand
% register or memory location
)
% Add with carry. Add 'adc_src' to 'adc_dst' + carry flag (CF).
% Cannot add two memory operands.
% Details on amd64-prog-man-vol3 manual p65.
; add(
add_src :: operand,
% immediate, register or memory location
add_dst :: operand
% register or memory location
)
% Signed or unsigned add. Add 'adc_src' to 'adc_dst'.
% Cannot add two memory operands.
% Details on amd64-prog-man-vol3 manual p67.
; and(
and_src :: operand,
% immediate, register or memory location
and_dst :: operand
% register or memory location
)
% Performs a bitwise AND operation on both operands.
% Cannot and two memory operands.
% Details on amd64-prog-man-vol3 manual p69.
; bs(
bs_src :: operand,
% register or memory location
bs_dst :: operand,
% register
bs_cond :: direction
% "F" for forward, "R" for reverse"
)
% Bit scan. Searches the value in 'bs_src' for the least
% significant set bit if 'bs_cond' is "F". Searches
% for the most significant set bit if 'bs_cond' is "R".
% Details on amd64-prog-man-vol3 manual p(74-75).
; bswap(
bswap_reg :: operand
)
% Byte swap. Reverses the byte order of the specified 'operand'.
% Details on amd64-prog-man-vol3 manual p76.
; bt(
bt_src :: operand,
% register or memory reference
bt_idx :: operand
% register or 8-bit immediate value
)
% Bit test. Copies a bit specified by 'bt_idx' from a bit string in
% 'bt_src' to the carry flag (CF) or RFLAGS register.
% Details on amd64-prog-man-vol3 manual p77.
; btc(
btc_src :: operand,
% register or memory reference
btc_idx :: operand
% register or 8-bit immediate value
)
% Bit test and complement. Complements 'btc_src' after performing
% 'bt' operation.
% Details on amd64-prog-man-vol3 manual p79.
; btr(
btr_src :: operand,
% register or memory reference
btr_idx :: operand
% register or 8-bit immediate value
)
% Bit test and reverse. Clears 'btr_src' to 0 after performing 'bt'
% operation.
% Details on amd64-prog-man-vol3 manual p81.
; bts(
bts_src :: operand,
% register or memory reference
bts_idx :: operand
% register or 8-bit immediate value
)
% Bit test and set. Sets 'bts_src' to 1 after performing 'bt'
% operation.
% Details on amd64-prog-man-vol3 manual p83.
; call(
call_target :: operand
% label, register, memory reference or rel offset
)
% Call with target specified by call_target
% Details on amd64-prog-man-vol3 manual p85.
; cbw
% Sign-extend AL into AX.
% Details on amd64-prog-man-vol3 manual p94.
; cwde
% Sign-extend AX into EAX.
% Details on amd64-prog-man-vol3 manual p94.
; cdqe
% Sign-extend EAX into RAX.
% Details on amd64-prog-man-vol3 manual p94.
; cwd
% Sign-extend AX into DX:AX.
% Details on amd64-prog-man-vol3 manual p95.
; cdq
% Sign-extend EAX into EDX:EAX.
% Details on amd64-prog-man-vol3 manual p95.
; cqo
% Sign-extend RAX into RDX:RAX.
% Details on amd64-prog-man-vol3 manual p95.
; clc
% Clears the carry flag (CF) in the rFLAGS register to zero.
% Details on amd64-prog-man-vol3 manual p96.
; cld
% Clears the direction flag (DF) in the rFLAGS register to zero.
% Details on amd64-prog-man-vol3 manual p97.
; cmc
% Complements the carry flag bit (CF) bit in the rFLAGS register.
% Details on amd64-prog-man-vol3 manual p100.
; cmov(
cmov_src :: operand,
% memory or register
cmov_dest :: operand,
% register
cmov_cmp_op :: condition
)
% Moves with comparison operation defined in 'cmov_cmp_op'.
% Details on amd64-prog-man-vol3 manual p(101-103).
; cmp(
cmp_src :: operand,
% register, memory location or immediate value
cmp_dest :: operand
% register or memory location
)
% Compares 'cmp_src' with 'cmp_dest'.
% Details on amd64-prog-man-vol3 manual p105.
; cmpxchg(
cmpxchg_src :: operand,
% register or memory location
cmpxchg_dest :: operand
% register
)
% Compares the value in RAX with the value in 'cmpxchg_dest'.
% If equal, copies the value in 'cmpxchg_src' to 'cmpxchg_dest'.
% Details on amd64-prog-man-vol3 manual p111.
; cmpxchg8b(
cmpxchg8b_mem :: operand
% memory reference
)
% Compares the value in EDX:EAX with the value in 'mem_ref'.
% Details on amd64-prog-man-vol3 manual p113.
; dec(
dec_op :: operand
% register or memory reference
)
% Decrements the contents of 'dec_op'.
% Details on amd64-prog-man-vol3 manual p120.
; div(
div_op :: operand
% register or memory reference
)
% Unsigned divide RDX:RAX by the value in 'div_op'.
% Details on amd64-prog-man-vol3 manual p122.
; enter(
enter_stack_size :: uint16,
enter_nesting_level :: uint8
)
% Creates a procedure stack frame with a stack size specified in
% 'enter_stack_size' and nesting level specified in
% 'enter_nesting_level'(0 to 31).
% Details on amd64-prog-man-vol3 manual p124.
; idiv(
idiv_op :: operand
% register or memory reference
)
% Signed divide RDX:RAX by the value in 'operand'.
% Details on amd64-prog-man-vol3 manual p126.
; imul(
imul_src :: operand,
% register, memory location, immediate value
imul_dest :: maybe(operand),
% register
imul_multiplicand :: maybe(operand)
)
% Signed multiply 'imul_src' by 'imul_dest' (if specified).
% Otherwise multiply 'imul_src' by the value in RAX register.
% Details on amd64-prog-man-vol3 manual p(128-129).
; inc(
inc_op :: operand
% register or memory location
)
% Increments the contents of 'inc_op'.
% Details on amd64-prog-man-vol3 manual p133.
; j(
j_target :: operand,
% relative offset
j_condition :: condition
)
% Conditional jump.
% Details on amd64-prog-man-vol3 manual p(147-149).
; jrcxz(
jrcxz_8bit_off :: operand
)
% Jumps to the target instruction located at 'jrcxz_8bit_off'
% if RCX is zero.
% Details on amd64-prog-man-vol3 manual p150.
; jmp(
jmp_op :: operand
)
% Jumps with target specified in 'jmp_op'
% Details on amd64-prog-man-vol3 manual p152.
; lea(
lea_src :: operand,
% memory location
lea_dest :: operand
% register
)
% Stores effective address 'lea_src' into 'lea_dest'.
% Details on amd64-prog-man-vol3 manual p163.
; leave
% Sets RSP to the value in RBP and pop RBP.
% Details on amd64-prog-man-vol3 manual p164.
; loop(
loop_rel_8bit :: operand
)
% Decrements RCX then jump if RCX is not zero
% Details on amd64-prog-man-vol3 manual p169.
; loope(
loope_rel_8bit :: operand
)
% Decrements RCX then jump if RCX is not zero and ZF = 1.
% Details on amd64-prog-man-vol3 manual p169.
; loopne(
loopne_rel_8bit :: operand
)
% Decrements RCX then jump if RCX is not zero and ZF = 0.
% Details on amd64-prog-man-vol3 manual p169.
; loopnz(
loopnz_rel_8bit :: operand
)
% Decrements RCX then jump if RCX is not zero and ZF = 0.
% Details on amd64-prog-man-vol3 manual p170.
; loopz(
loopz_rel_8bit :: operand
)
% Decrements RCX then jump if RCX is not zero and ZF = 1.
% Details on amd64-prog-man-vol3 manual p170.
; mov(
mov_src :: operand,
% register, memory location or immediate value
mov_dest :: operand
% register or memory location
)
% Copies 'mov_src' to 'mov_dest'. 'mov_dest' cannot be immediate op.
% Details on amd64-prog-man-vol3 manual p173.
; mul(
mul_op :: operand
% register or memory location
)
% Unsigned multiply 'mul_op' by RAX.
% Details on amd64-prog-man-vol3 manual p190.
; neg(
neg_op :: operand
% register or memory location
)
% Performs a two's complement negation of 'operand'.
% Details on amd64-prog-man-vol3 manual p192.
; nop
% Increments RIP to point to next instruction.
% Details on amd64-prog-man-vol3 manual p194.
; x86_64_instr_not(
not_op :: operand
% register or memory location
)
% Performs one's complement negation (NOT) of 'operand'.
% Details on amd64-prog-man-vol3 manual p195.
; or(
or_src :: operand,
% register, memory location or immediate value
or_dest :: operand
% register or memory location
)
% Performs a logical OR on the bits in 'or_src' and 'or_dest'.
% Details on amd64-prog-man-vol3 manual p196.
; pop(
pop_op :: operand
% register or memory location.
)
% Pops the stack into 'operand'.
% Details on amd64-prog-man-vol3 manual p204.
; popfq
% Pops a quadword from the stack to the RFLAGS register.
% Details on amd64-prog-man-vol3 manual p208.
; push(
push_op :: operand
% register, memory location or immediate value
)
% Pushes the content of operand onto the stack.
% Details on amd64-prog-man-vol3 manual p215.
; pushfq
% Pushes the RFLAGS quadword onto the stack.
% Details on amd64-prog-man-vol3 manual p218.
; rc(
rc_amount :: operand,
% unsigned immediate value or register
rc_dest :: operand,
% register or memory location
rc_cond :: string
% "R" for right, "L" for left
)
% Rotate bits in 'rc_dest' according to 'rc_cond' direction
% and through the carry flag by the number of bit positions as
% specified in 'rc_amount'.
% Details on amd64-prog-man-vol3 manual p(220-222).
; ret(
ret_op :: maybe(uint16)
)
% Near return to the calling procedure, then pop the specified
% number of bytes from stack (if specified).
% Details on amd64-prog-man-vol3 manual p224.
; ro(
ro_amount :: operand,
% unsigned immediate value or a register
ro_dest :: operand,
% register or memory location
ro_dir :: string
% "L" for left, "R" for right
)
% Rotates the bits of 'rol_dest' to the 'ro_dir' direction by
% 'rol_amount' bits.
% Details on amd64-prog-man-vol3 manual p(230-232).
; sal(
sal_amount :: operand,
% unsigned immediate value or register
sal_dest :: operand
% register or memory location
)
% Shift 'sal_dest' left by 'sal_amount'.
% Details on amd64-prog-man-vol3 manual p235.
; shl(
shl_amount :: operand,
% unsigned immediate value or register
shl_dest :: operand
% register or memory location
)
% Alias to 'sal'.
% Details on amd64-prog-man-vol3 manual p235.
; sar(
sar_amount :: operand,
% unsigned immediate value or register
sar_dest :: operand
% register or memory location
)
% Shift 'sar_dest' right by 'sar_amount'.
% Details on amd64-prog-man-vol3 manual p238.
; sbb(
sbb_src :: operand,
% immediate value, register or memory location
sbb_dest :: operand
% register or memory location
)
% Subtracts 'sbb_src' from 'sbb_dest' with borrow.
% Details on amd64-prog-man-vol3 manual p241.
; set(
set_dest :: operand,
% register or memory location
set_cond :: condition
)
% Check the status flag in the RFLAGS register. Set the value in
% the specified register/8-bit memory reference in 'set_dest' to 1
% according to the 'set_cond'.
% Details on amd64-prog-man-vol3 manual p(246-247).
; shld(
shld_amount :: operand,
% register or immediate value
shld_dest1 :: operand,
% register or memory location
shld_dest2 :: operand
% register
)
% Shift 'shld_dest1' to the left by 'shld_amount' and shift in a bit
% pattern in 'shld_dest2' from the right.
% Details on amd64-prog-man-vol3 manual p251.
; shr(
shr_amount :: operand,
% register or immediate value
shr_dest :: operand
% register or memory location
)
% Shift 'shr_dest' right by 'shr_amount'.
% Details on amd64-prog-man-vol3 manual p253.
; shrd(
shrd_amount :: operand,
% register or immediate value
shrd_dest1 :: operand,
% register or memory location
shrd_dest2 :: operand
% register
)
% Shift 'shrd_dest1' to the right by 'shrd_amount' and shift in
% a bit pattern in 'shrd_dest2' from the left.
% Details on amd64-prog-man-vol3 manual p255.
; stc
% Sets the carry flag (CF) in the RFLAGS register to 1.
% Details on amd64-prog-man-vol3 manual p257.
; std
% Sets the direction flag (DF) in the rflags register to 1.
% Details on amd64-prog-man-vol3 manual p258.
; sub(
sub_src :: operand,
% imm value, register or memory location
sub_dest :: operand
% register or memory location
)
% Subtracts 'sub_src' from 'sub_dest'.
% Details on amd64-prog-man-vol3 manual p261.
; test(
test_src1 :: operand,
% imm value or register
test_src2 :: operand
% register or memory location
)
% Performs a bitwise AND on 'test_src1' and 'test_src2'.
% Details on amd64-prog-man-vol3 manual p264.
; xadd(
xadd_src :: operand,
% register
xadd_dest :: operand
% register or memory location
)
% Exchanges the contents of 'xadd_src' with 'xadd_dest', load
% their sum into 'xadd_dest'.
% Details on amd64-prog-man-vol3 manual p266.
; xchg(
xchg_src1 :: operand,
% register or memory location
xchg_src2 :: operand
% register or memory location
)
% Exchanges the contents of 'xchg_src1' and 'xchg_src2'.
% Details on amd64-prog-man-vol3 manual p268.
; xor(
xor_src :: operand,
xor_dest :: operand
).
% Performs a bitwise XOR on 'xor_src' with 'xor_dest' and stores
% the result in xor_dest.
% Details on amd64-prog-man-vol3 manual p272.
%-----------------------------------------------------------------------------%
% Returns the number of x86_64 general-purpose registers.
%
:- func num_x86_64_regs = int.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
init_x86_64_module(CModule) = x86_64_module(CModule, []).
init_x86_64_proc(CProc) =
x86_64_procedure(CProc ^ cproc_name, CProc ^ cproc_orig_arity,
CProc ^ cproc_id, CProc ^ cproc_code_model, [],
CProc ^ cproc_proc_label, CProc ^ cproc_label_nums,
CProc ^ cproc_may_alter_rtti, CProc ^ cproc_c_global_vars).
init_x86_64_instruction = x86_64_instr([], "").
num_x86_64_regs = 16.
%-----------------------------------------------------------------------------%
:- end_module ll_backend.x86_64_instrs.
%----------------------------------------------------------------------------%