X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fia32%2Fia32_spec.pl;h=f4c6328aa3561886e896c899d37d1043d40055fd;hb=a1a465eb2b3f54027b29f829423fffd0396937f4;hp=72f273c45df5a348776f9804337df623ec5f0b67;hpb=88036a81928ac4e2246c29dcf41eeddf508e8429;p=libfirm diff --git a/ir/be/ia32/ia32_spec.pl b/ir/be/ia32/ia32_spec.pl index 72f273c45..f4c6328aa 100644 --- a/ir/be/ia32/ia32_spec.pl +++ b/ir/be/ia32/ia32_spec.pl @@ -73,6 +73,10 @@ $comment_string = "/*"; # # outs: if a node defines more than one output, the names of the projections # nodes having outs having automatically the mode mode_T +# One can also annotate some flags for each out, additional to irn_flags. +# They are separated from name with a colon ':', and concatenated by pipe '|' +# Only I and S are available at the moment (same meaning as in irn_flags). +# example: [ "frame:I", "stack:I|S", "M" ] # # comment: OPTIONAL comment for the node constructor # @@ -100,6 +104,8 @@ $comment_string = "/*"; # 1 - caller save (register must be saved by the caller of a function) # 2 - callee save (register must be saved by the called function) # 4 - ignore (do not assign this register) +# 8 - emitter can choose an arbitrary register of this class +# 16 - the register is a virtual one # NOTE: Last entry of each class is the largest Firm-Mode a register can hold %reg_classes = ( "gp" => [ @@ -133,8 +139,8 @@ $comment_string = "/*"; # { "name" => "r32", "type" => 1 }, { "name" => "ebp", "type" => 2 }, { "name" => "esp", "type" => 4 }, - { "name" => "gp_NOREG", "type" => 6 }, # we need a dummy register for NoReg nodes - { "name" => "gp_UKNWN", "type" => 6 }, # we need a dummy register for Unknown nodes + { "name" => "gp_NOREG", "type" => 2 | 4 | 16 }, # we need a dummy register for NoReg nodes + { "name" => "gp_UKNWN", "type" => 2 | 4 | 8 | 16}, # we need a dummy register for Unknown nodes { "mode" => "mode_P" } ], "xmm" => [ @@ -146,21 +152,21 @@ $comment_string = "/*"; { "name" => "xmm5", "type" => 1 }, { "name" => "xmm6", "type" => 1 }, { "name" => "xmm7", "type" => 1 }, - { "name" => "xmm_NOREG", "type" => 6 }, # we need a dummy register for NoReg nodes - { "name" => "xmm_UKNWN", "type" => 6 }, # we need a dummy register for Unknown nodes + { "name" => "xmm_NOREG", "type" => 2 | 4 | 16 }, # we need a dummy register for NoReg nodes + { "name" => "xmm_UKNWN", "type" => 2 | 4 | 8 | 16}, # we need a dummy register for Unknown nodes { "mode" => "mode_D" } ], "vfp" => [ - { "name" => "vf0", "type" => 1 }, - { "name" => "vf1", "type" => 1 }, - { "name" => "vf2", "type" => 1 }, - { "name" => "vf3", "type" => 1 }, - { "name" => "vf4", "type" => 1 }, - { "name" => "vf5", "type" => 1 }, - { "name" => "vf6", "type" => 1 }, - { "name" => "vf7", "type" => 1 }, - { "name" => "vfp_NOREG", "type" => 6 }, # we need a dummy register for NoReg nodes - { "name" => "vfp_UKNWN", "type" => 6 }, # we need a dummy register for Unknown nodes + { "name" => "vf0", "type" => 1 | 16 }, + { "name" => "vf1", "type" => 1 | 16 }, + { "name" => "vf2", "type" => 1 | 16 }, + { "name" => "vf3", "type" => 1 | 16 }, + { "name" => "vf4", "type" => 1 | 16 }, + { "name" => "vf5", "type" => 1 | 16 }, + { "name" => "vf6", "type" => 1 | 16 }, + { "name" => "vf7", "type" => 1 | 16 }, + { "name" => "vfp_NOREG", "type" => 2 | 4 | 16 }, # we need a dummy register for NoReg nodes + { "name" => "vfp_UKNWN", "type" => 2 | 4 | 8 | 16}, # we need a dummy register for Unknown nodes { "mode" => "mode_E" } ], "st" => [ @@ -246,6 +252,8 @@ $comment_string = "/*"; }, "MulS" => { + # we should not rematrialize this node. It produces 2 results and has + # very strict constrains "comment" => "construct MulS: MulS(a, b) = MulS(b, a) = a * b", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", "reg_req" => { "in" => [ "gp", "gp", "eax", "gp", "none" ], "out" => [ "eax", "edx" ] }, @@ -255,6 +263,8 @@ $comment_string = "/*"; }, "l_MulS" => { + # we should not rematrialize this node. It produces 2 results and has + # very strict constrains "op_flags" => "C", "cmp_attr" => " return 1;\n", "comment" => "construct lowered MulS: MulS(a, b) = MulS(b, a) = a * b", @@ -281,6 +291,8 @@ $comment_string = "/*"; # Mulh is an exception from the 4 INs with AM because the target is always EAX:EDX "Mulh" => { + # we should not rematrialize this node. It produces 2 results and has + # very strict constrains "comment" => "construct Mul: Mul(a, b) = Mul(b, a) = a * b", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", "reg_req" => { "in" => [ "gp", "gp", "eax", "gp", "none" ], "out" => [ "eax", "edx" ] }, @@ -410,7 +422,7 @@ $comment_string = "/*"; "irn_flags" => "R", "comment" => "construct Shl: Shl(a, b) = a << b", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "gp", "gp", "gp", "ecx", "none" ], "out" => [ "in_r3 !in_r4" ] }, + "reg_req" => { "in" => [ "gp", "gp", "gp", "ecx gp_NOREG", "none" ], "out" => [ "in_r3 !in_r4" ] }, "emit" => '. shl %ia32_emit_binop /* Shl(%A1, %A2) -> %D1 */', "outs" => [ "res", "M" ], }, @@ -425,7 +437,15 @@ $comment_string = "/*"; "irn_flags" => "R", "comment" => "construct ShlD: ShlD(a, b, c) = a, b << count (shift left count bits from b into a)", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "gp", "gp", "gp", "gp", "ecx", "none" ], "out" => [ "in_r3 !in_r5" ] }, + # Out requirements is: different from all in + # This is because, out must be different from LowPart and ShiftCount. + # We could say "!ecx !in_r4" but it can occur, that all values live through + # this Shift and the only value dying is the ShiftCount. Then there would be a + # register missing, as result must not be ecx and all other registers are + # occupied. What we should write is "!in_r4 !in_r5", but this is not supported + # (and probably never will). So we create artificial interferences of the result + # with all inputs, so the spiller can always assure a free register. + "reg_req" => { "in" => [ "gp", "gp", "gp", "gp", "ecx", "none" ], "out" => [ "!in" ] }, "emit" => ' if (get_ia32_immop_type(n) == ia32_ImmNone) { @@ -459,7 +479,7 @@ else { "irn_flags" => "R", "comment" => "construct Shr: Shr(a, b) = a >> b", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "gp", "gp", "gp", "ecx", "none" ], "out" => [ "in_r3 !in_r4" ] }, + "reg_req" => { "in" => [ "gp", "gp", "gp", "ecx gp_NOREG", "none" ], "out" => [ "in_r3 !in_r4" ] }, "emit" => '. shr %ia32_emit_binop /* Shr(%A1, %A2) -> %D1 */', "outs" => [ "res", "M" ], }, @@ -474,7 +494,15 @@ else { "irn_flags" => "R", "comment" => "construct ShrD: ShrD(a, b, c) = a, b >> count (shift rigth count bits from a into b)", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "gp", "gp", "gp", "gp", "ecx", "none" ], "out" => [ "in_r3 !in_r5" ] }, + # Out requirements is: different from all in + # This is because, out must be different from LowPart and ShiftCount. + # We could say "!ecx !in_r4" but it can occur, that all values live through + # this Shift and the only value dying is the ShiftCount. Then there would be a + # register missing, as result must not be ecx and all other registers are + # occupied. What we should write is "!in_r4 !in_r5", but this is not supported + # (and probably never will). So we create artificial interferences of the result + # with all inputs, so the spiller can always assure a free register. + "reg_req" => { "in" => [ "gp", "gp", "gp", "gp", "ecx", "none" ], "out" => [ "!in" ] }, "emit" => ' if (get_ia32_immop_type(n) == ia32_ImmNone) { @@ -508,7 +536,7 @@ else { "irn_flags" => "R", "comment" => "construct Shrs: Shrs(a, b) = a >> b", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "gp", "gp", "gp", "ecx", "none" ], "out" => [ "in_r3 !in_r4" ] }, + "reg_req" => { "in" => [ "gp", "gp", "gp", "ecx gp_NOREG", "none" ], "out" => [ "in_r3 !in_r4" ] }, "emit" => '. sar %ia32_emit_binop /* Shrs(%A1, %A2) -> %D1 */', "outs" => [ "res", "M" ], }, @@ -629,11 +657,12 @@ else { "irn_flags" => "R", "comment" => "represents an integer constant", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "none" ], "out" => [ "gp" ] }, + "reg_req" => { "out" => [ "gp" ] }, }, "Cdq" => { - "irn_flags" => "R", + # we should not rematrialize this node. It produces 2 results and has + # very strict constrains "comment" => "construct CDQ: sign extend EAX -> EDX:EAX", "reg_req" => { "in" => [ "gp" ], "out" => [ "eax in_r1", "edx" ] }, "emit" => '. cdq /* sign extend EAX -> EDX:EAX, (%A1) */', @@ -693,7 +722,7 @@ else { "state" => "exc_pinned", "comment" => "construct 8Bit Store: Store(ptr, val, mem) = ST ptr,val", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "gp", "gp", "eax ebx ecx edx", "none" ] }, + "reg_req" => { "in" => [ "gp", "gp", "eax ebx ecx edx gp_NOREG", "none" ] }, "emit" => '. mov %ia32_emit_binop /* Store(%A3) -> (%A1) */', "outs" => [ "M" ], "latency" => 3, @@ -709,41 +738,19 @@ else { }, "Push" => { - # We don't set class modify_stack here (but we will do this on proj 0) - "comment" => "push a gp register on the stack", - "reg_req" => { "in" => [ "esp", "gp", "none" ], "out" => [ "esp" ] }, - "emit" => ' -if (get_ia32_id_cnst(n)) { - if (get_ia32_immop_type(n) == ia32_ImmConst) { -4. push %C /* Push const on stack */ -} else { -4. push OFFSET FLAT:%C /* Push symconst on stack */ - } -} -else if (get_ia32_op_type(n) == ia32_Normal) { -2. push %S2 /* Push(%A2) */ -} -else { -2. push %ia32_emit_am /* Push memory to stack */ -}; -', - "outs" => [ "stack", "M" ], + "comment" => "push on the stack", + "reg_req" => { "in" => [ "gp", "gp", "gp", "esp", "none" ], "out" => [ "esp" ] }, + "emit" => '. push %ia32_emit_unop /* PUSH(%A1) */', + "outs" => [ "stack:I|S", "M" ], "latency" => 3, }, "Pop" => { # We don't set class modify stack here (but we will do this on proj 1) "comment" => "pop a gp register from the stack", - "reg_req" => { "in" => [ "esp", "none" ], "out" => [ "gp", "esp" ] }, - "emit" => ' -if (get_ia32_op_type(n) == ia32_Normal) { -2. pop %D1 /* Pop from stack into %D1 */ -} -else { -2. pop %ia32_emit_am /* Pop from stack into memory */ -} -', - "outs" => [ "res", "stack", "M" ], + "reg_req" => { "in" => [ "gp", "gp", "esp", "none" ], "out" => [ "gp", "esp" ] }, + "emit" => '. pop %ia32_emit_unop /* POP(%A1) */', + "outs" => [ "res", "stack:I|S", "M" ], "latency" => 4, }, @@ -751,7 +758,7 @@ else { "comment" => "create stack frame", "reg_req" => { "in" => [ "esp" ], "out" => [ "ebp", "esp" ] }, "emit" => '. enter /* Enter */', - "outs" => [ "frame", "stack", "M" ], + "outs" => [ "frame:I", "stack:I|S", "M" ], "latency" => 15, }, @@ -759,7 +766,7 @@ else { "comment" => "destroy stack frame", "reg_req" => { "in" => [ "esp", "ebp" ], "out" => [ "ebp", "esp" ] }, "emit" => '. leave /* Leave */', - "outs" => [ "frame", "stack", "M" ], + "outs" => [ "frame:I", "stack:I|S", "M" ], "latency" => 3, }, @@ -767,9 +774,24 @@ else { "irn_flags" => "I", "comment" => "allocate space on stack", "reg_req" => { "in" => [ "esp", "gp" ], "out" => [ "esp", "none" ] }, - "outs" => [ "stack", "M" ], + "outs" => [ "stack:S", "M" ], +}, + +"SubSP" => { + "irn_flags" => "I", + "comment" => "free space on stack", + "reg_req" => { "in" => [ "esp", "gp" ], "out" => [ "esp", "none" ] }, + "outs" => [ "stack:S", "M" ], +}, + +"LdTls" => { + "irn_flags" => "R", + "comment" => "get the TLS base address", + "reg_req" => { "out" => [ "gp" ] }, }, + + #-----------------------------------------------------------------------------# # _____ _____ ______ __ _ _ _ # # / ____/ ____| ____| / _| | | | | | # @@ -906,7 +928,7 @@ else { "irn_flags" => "R", "comment" => "represents a SSE constant", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "none" ], "out" => [ "xmm" ] }, + "reg_req" => { "out" => [ "xmm" ] }, "emit" => '. movs%M %D1, %C /* Load fConst into register */', "latency" => 2, }, @@ -978,7 +1000,7 @@ else { "state" => "exc_pinned", "comment" => "load ST0 from stack", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "gp", "none" ], "out" => [ "st0", "none" ] }, + "reg_req" => { "in" => [ "gp", "none" ], "out" => [ "vf0", "none" ] }, "emit" => '. fld %ia32_emit_am /* load ST0 from stack */', "outs" => [ "res", "M" ], "latency" => 2, @@ -1324,7 +1346,7 @@ else { "init_attr" => " set_ia32_ls_mode(res, mode);", "comment" => "represents a virtual floating point constant", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "in" => [ "none" ], "out" => [ "vfp" ] }, + "reg_req" => { "out" => [ "vfp" ] }, "latency" => 3, }, @@ -1543,65 +1565,58 @@ else { # constants "fldz" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load 0.0: Ld 0.0 -> reg", - "reg_req" => { }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fldz /* x87 0.0 -> %D1 */', }, "fld1" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load 1.0: Ld 1.0 -> reg", - "reg_req" => { }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fld1 /* x87 1.0 -> %D1 */', }, "fldpi" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load pi: Ld pi -> reg", - "reg_req" => { }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fldpi /* x87 pi -> %D1 */', }, "fldln2" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load ln 2: Ld ln 2 -> reg", - "reg_req" => { }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fldln2 /* x87 ln(2) -> %D1 */', }, "fldlg2" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load lg 2: Ld lg 2 -> reg", - "reg_req" => { }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fldlg2 /* x87 log(2) -> %D1 */', }, "fldl2t" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load ld 10: Ld ld 10 -> reg", - "reg_req" => { }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fldll2t /* x87 ld(10) -> %D1 */', }, "fldl2e" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load ld e: Ld ld e -> reg", - "reg_req" => { }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fldl2e /* x87 ld(e) -> %D1 */', }, @@ -1611,25 +1626,34 @@ else { "rd_constructor" => "NONE", "comment" => "represents a x87 constant", "cmp_attr" => " return ia32_compare_immop_attr(attr_a, attr_b);\n", - "reg_req" => { "out" => [ "st" ] }, + "reg_req" => { "out" => [ "vfp" ] }, "emit" => '. fld %ia32_emit_adr /* Load fConst into register -> %D1 */', }, # fxch, fpush, fpop # Note that it is NEVER allowed to do CSE on these nodes +# Moreover, note the virtual register requierements! "fxch" => { "op_flags" => "R|K", "comment" => "x87 stack exchange", - "reg_req" => { "in" => [ "st"], "out" => [ "st" ] }, + "reg_req" => { }, "cmp_attr" => " return 1;\n", "emit" => '. fxch %X1 /* x87 swap %X1, %X3 */', }, "fpush" => { + "op_flags" => "R|K", + "comment" => "x87 stack push", + "reg_req" => {}, + "cmp_attr" => " return 1;\n", + "emit" => '. fld %X1 /* x87 push %X1 */', +}, + +"fpushCopy" => { "op_flags" => "R", "comment" => "x87 stack push", - "reg_req" => { "in" => [ "st"], "out" => [ "st" ] }, + "reg_req" => { "in" => [ "vfp"], "out" => [ "vfp" ] }, "cmp_attr" => " return 1;\n", "emit" => '. fld %X1 /* x87 push %X1 */', }, @@ -1637,7 +1661,7 @@ else { "fpop" => { "op_flags" => "R|K", "comment" => "x87 stack pop", - "reg_req" => { "out" => [ "st" ] }, + "reg_req" => { }, "cmp_attr" => " return 1;\n", "emit" => '. fstp %X1 /* x87 pop %X1 */', },