class Metasm::X86_64::CCompiler

Public Class Methods

new(*a) click to toggle source
Calls superclass method Metasm::C::Compiler::new
# File metasm/cpu/x86_64/compile_c.rb, line 71
def initialize(*a)
        super(*a)
        @cpusz = 64
        @regnummax = 15
end

Public Instance Methods

c_asm(stmt) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 931
def c_asm(stmt)
        if stmt.output or stmt.input or stmt.clobber
                raise # TODO (handle %%0 => rax, gas, etc)
        else
                raise 'asm refering variables unhandled' if @state.func.initializer.symbol.keys.find { |sym| stmt.body =~ /\b#{Regexp.escape(sym)}\b/ }
                @source << stmt.body
        end
end
c_block_exit(block) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 853
def c_block_exit(block)
        @state.cache.delete_if { |k, v|
                case v
                when C::Variable; block.symbol.index v
                when Address; block.symbol.index v.target
                end
        }
        block.symbol.each { |s|
                unuse @state.bound.delete(s)
        }
end
c_cexpr(expr) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 842
def c_cexpr(expr)
        case expr.op
        when :+, :-, :*, :/, :&, :|, :^, :%, :[], nil, :'.', :'->',
                :>, :<, :<=, :>=, :==, :'!=', :'!'
                # skip no-ops
                c_cexpr(expr.lexpr) if expr.lexpr.kind_of? C::CExpression
                c_cexpr(expr.rexpr) if expr.rexpr.kind_of? C::CExpression
        else unuse c_cexpr_inner(expr)
        end
end
c_cexpr_inner(expr) click to toggle source

compiles a c expression, returns an X64 instruction argument

# File metasm/cpu/x86_64/compile_c.rb, line 287
def c_cexpr_inner(expr)
        case expr
        when ::Integer; Expression[expr]
        when C::Variable; findvar(expr)
        when C::CExpression
                if not expr.lexpr or not expr.rexpr
                        inuse c_cexpr_inner_nol(expr)
                else
                        inuse c_cexpr_inner_l(expr)
                end
        when C::Label; findvar(C::Variable.new(expr.name, C::Array.new(C::BaseType.new(:void), 1)))
        else puts "c_ce_i: unsupported #{expr}" if $VERBOSE
        end
end
c_cexpr_inner_arith(l, op, r, type) click to toggle source

compiles/optimizes arithmetic operations

# File metasm/cpu/x86_64/compile_c.rb, line 716
def c_cexpr_inner_arith(l, op, r, type)
        # optimizes *2 -> <<1
        if r.kind_of? Expression and (rr = r.reduce).kind_of? ::Integer
                if type.integral? or type.pointer?
                        log2 = lambda { |v|
                                # TODO lol
                                i = 0
                                i += 1 while (1 << i) < v
                                i if (1 << i) == v
                        }
                        if (lr = log2[rr]).kind_of? ::Integer
                                case op
                                when :*; return c_cexpr_inner_arith(l, :<<, Expression[lr], type)
                                when :/; return c_cexpr_inner_arith(l, :>>, Expression[lr], type)
                                when :%; return c_cexpr_inner_arith(l, :&, Expression[rr-1], type)
                                end
                        else
                                # TODO /r => *(r^(-1)), *3 => stuff with magic constants..
                        end
                end
        end

        if type.float?
                raise 'float unhandled'
        else
                c_cexpr_inner_arith_int(l, op, r, type)
        end
end
c_cexpr_inner_arith_int(l, op, r, type) click to toggle source

compile an integral arithmetic expression, reg-sized

# File metasm/cpu/x86_64/compile_c.rb, line 746
def c_cexpr_inner_arith_int(l, op, r, type)
        op = case op
        when :+; 'add'
        when :-; 'sub'
        when :&; 'and'
        when :|; 'or'
        when :^; 'xor'
        when :>>; type.specifier == :unsigned ? 'shr' : 'sar'
        when :<<; 'shl'
        when :*; 'mul'
        when :/; 'div'
        when :%; 'mod'
        end

        case op
        when 'add', 'sub', 'and', 'or', 'xor'
                r = make_volatile(r, type) if l.kind_of?(ModRM) and r.kind_of?(ModRM)
                r = make_volatile(r, type) if r.kind_of?(ModRM) and r.sz != l.sz     # add rax, word [moo]
                unuse r
                r = Reg.new(r.val, l.sz) if r.kind_of?(Reg) and l.kind_of?(ModRM) and l.sz and l.sz != r.sz     # add byte ptr [rax], bl
                instr op, l, i_to_i32(r)
        when 'shr', 'sar', 'shl'
                if r.kind_of? Expression
                        instr op, l, r
                else
                        # XXX bouh
                        r = make_volatile(r, C::BaseType.new(:__int8, :unsigned))
                        unuse r
                        if r.val != 1
                                rcx = Reg.new(1, 64)
                                instr 'xchg', rcx, Reg.new(r.val, 64)
                                l = Reg.new(r.val, l.sz) if l.kind_of? Reg and l.val == 1
                        end
                        instr op, l, Reg.new(1, 8)
                        instr 'xchg', rcx, Reg.new(r.val, 64) if r.val != 1
                end
        when 'mul'
                if l.kind_of? ModRM
                        if r.kind_of? Expression
                                ll = findreg
                                instr 'imul', ll, l, r
                        else
                                ll = make_volatile(l, type)
                                unuse ll
                                instr 'imul', ll, r
                        end
                        instr 'mov', l, ll
                else
                        r = make_volatile(r, type) if r.kind_of?(ModRM) and r.sz != l.sz
                        instr 'imul', l, r
                end
                unuse r
        when 'div', 'mod'
                lv = l.val if l.kind_of? Reg
                rax = Reg.from_str 'rax'
                rdx = Reg.from_str 'rdx'
                if @state.used.include? rax.val and lv != rax.val
                        instr 'push', rax
                        saved_rax = true
                end
                if @state.used.include? rdx.val and lv != rdx.val
                        instr 'push', rdx
                        saved_rdx = true
                end

                instr 'mov', rax, l if lv != rax.val

                if r.kind_of? Expression
                        instr 'push', r
                        rsp = Reg.from_str 'rsp'
                        r = ModRM.new(@cpusz, 64, nil, nil, rsp, nil)
                        need_pop = true
                end

                if type.specifier == :unsigned
                        instr 'mov', rdx, Expression[0]
                        instr 'div', r
                else
                        instr 'cdq'
                        instr 'idiv', r
                end
                unuse r

                instr 'add', rsp, 8 if need_pop

                if op == 'div'
                        instr 'mov', l, rax if lv != rax.val
                else
                        instr 'mov', l, rdx if lv != rdx.val
                end

                instr 'pop', rdx if saved_rdx
                instr 'pop', rax if saved_rax
        end
end
c_cexpr_inner_cast(expr, r) click to toggle source

compile a cast (BaseType to BaseType)

# File metasm/cpu/x86_64/compile_c.rb, line 381
def c_cexpr_inner_cast(expr, r)
        if expr.type.float? or expr.rexpr.type.float?
                raise 'float unhandled'
        elsif (expr.type.integral? or expr.type.pointer?) and (expr.rexpr.type.integral? or expr.rexpr.type.pointer?)
                tto   = typesize[expr.type.pointer? ? :ptr : expr.type.name]*8
                tfrom = typesize[expr.rexpr.type.pointer? ? :ptr : expr.rexpr.type.name]*8
                r = resolve_address r if r.kind_of? Address
                if r.kind_of? Expression
                        r = make_volatile r, expr.type
                elsif tfrom > tto
                        case r
                        when ModRM
                                unuse r
                                r = r.dup
                                r.sz = tto
                                inuse r
                        when Reg
                                if r.sz == 64 and tto == 32
                                        instr 'mov', Reg.new(r.val, tto), Reg.new(r.val, tto)
                                else
                                        instr 'and', r, Expression[(1<<tto)-1] if r.sz > tto
                                end
                        end
                elsif tto > tfrom
                        if not r.kind_of? Reg or r.sz != @cpusz
                                unuse r
                                reg = inuse findreg
                                op = (r.sz == reg.sz ? 'mov' : (expr.rexpr.type.specifier == :unsigned ? 'movzx' : 'movsx'))
                                if reg.sz == 64 and r.sz == 32
                                        if op == 'movsx'
                                                instr 'movsxd', reg, r
                                        else
                                                instr 'mov', Reg.new(reg.val, 32), r
                                        end
                                else
                                        instr op, reg, r
                                end
                                r = reg
                        end
                end
        end
        r
end
c_cexpr_inner_funcall(expr) click to toggle source

compiles a subroutine call

# File metasm/cpu/x86_64/compile_c.rb, line 615
def c_cexpr_inner_funcall(expr)
        backup = []
        rax = Reg.new(0, 64)

        ft = expr.lexpr.type
        ft = ft.pointed if ft.pointer?
        ft = nil if not ft.kind_of? C::Function

        regargsmask = @state.regargs.dup
        if ft
                ft.args.each_with_index { |a, i|
                        if rn = a.has_attribute_var('register')
                                regargsmask.insert(i, Reg.from_str(rn).val)
                        end
                }
        end
        regargsmask = regargsmask[0, expr.rexpr.length]

        (@state.abi_flushregs_call | regargsmask.compact.uniq).each { |reg|
                next if reg == 4
                next if reg == 5 and @state.saved_rbp
                if not @state.used.include? reg
                        if not @state.abi_trashregs.include? reg
                                @state.dirty |= [reg]
                        end
                        next
                end
                backup << reg
                instr 'push', Reg.new(reg, 64)
                @state.used.delete reg
        }

        stackargs = expr.rexpr.zip(regargsmask).map { |a, r| a if not r }.compact

        # preserve 16byte stack align under windows
        stackalign = true if @state.args_space > 0 and (stackargs + backup).length & 1 == 1
        instr 'sub', Reg.new(4, @cpusz), Expression[8] if stackalign

        stackargs.reverse_each { |arg|
                raise 'arg unhandled' if not arg.type.integral? or arg.type.pointer?
                a = c_cexpr_inner(arg)
                a = resolve_address a if a.kind_of? Address
                a = make_volatile(a, arg.type) if a.kind_of? ModRM and arg.type.name != :__int64
                unuse a
                instr 'push', a
        }

        regargs_unuse = []
        regargsmask.zip(expr.rexpr).reverse_each { |ra, arg|
                next if not arg or not ra
                a = c_cexpr_inner(arg)
                a = resolve_address a if a.kind_of? Address
                r = Reg.new(ra, a.respond_to?(:sz) ? a.sz : 64)
                instr 'mov', r, a if not a.kind_of? Reg or a.val != r.val
                unuse a
                regargs_unuse << r if not @state.inuse.include? ra
                inuse r
        }
        instr 'sub', Reg.new(4, 64), Expression[@state.args_space] if @state.args_space > 0   # TODO prealloc that at func start

        if ft.kind_of? C::Function and ft.varargs and @state.args_space == 0
                # gcc stores here the nr of xmm args passed, real args are passed the standard way
                instr 'xor', rax, rax
                inuse rax
                regargs_unuse << rax
        end


        if expr.lexpr.kind_of? C::Variable and expr.lexpr.type.kind_of? C::Function
                instr 'call', Expression[expr.lexpr.name]
        else
                ptr = c_cexpr_inner(expr.lexpr)
                unuse ptr
                ptr = make_volatile(ptr, expr.lexpr.type) if ptr.kind_of? Address
                instr 'call', ptr
        end
        regargs_unuse.each { |r| unuse r }
        argsz = @state.args_space + stackargs.length * 8
        argsz += 8 if stackalign
        instr 'add', Reg.new(4, @cpusz), Expression[argsz] if argsz > 0

        @state.abi_flushregs_call.each { |reg| flushcachereg reg }
        @state.used |= backup
        if @state.used.include?(0)
                retreg = inuse findreg
        else
                retreg = inuse getreg(0)
        end
        backup.reverse_each { |reg|
                if retreg.kind_of? Reg and reg == 0
                        instr 'pop', Reg.new(retreg.val, 64)
                        instr 'xchg', Reg.new(reg, 64), Reg.new(retreg.val, 64)
                else
                        instr 'pop', Reg.new(reg, 64)
                end
                inuse getreg(reg)
        }
        retreg
end
c_cexpr_inner_l(expr) click to toggle source

compiles a CExpression, not arithmetic (assignment, comparison etc)

# File metasm/cpu/x86_64/compile_c.rb, line 426
def c_cexpr_inner_l(expr)
        case expr.op
        when :funcall
                c_cexpr_inner_funcall(expr)
        when :'+=', :'-=', :'*=', :'/=', :'%=', :'^=', :'&=', :'|=', :'<<=', :'>>='
                l = c_cexpr_inner(expr.lexpr)
                raise 'bad lvalue' if not l.kind_of? ModRM and not @state.bound.index(l)
                r = c_cexpr_inner(expr.rexpr)
                op = expr.op.to_s.chop.to_sym
                c_cexpr_inner_arith(l, op, r, expr.type)
                l
        when :'+', :'-', :'*', :'/', :'%', :'^', :'&', :'|', :'<<', :'>>'
                # both sides are already cast to the same type by the precompiler
                # XXX fptrs are not #integral? ...
                if expr.type.integral? and expr.type.name == :ptr and expr.lexpr.type.kind_of? C::BaseType and
                        typesize[expr.lexpr.type.name] == typesize[:ptr]
                        expr.lexpr.type.name = :ptr
                end
                l = c_cexpr_inner(expr.lexpr)
                l = make_volatile(l, expr.type) if not l.kind_of? Address
                if expr.type.integral? and expr.type.name == :ptr and l.kind_of? Reg
                        unuse l
                        l = Address.new ModRM.new(l.sz, @cpusz, nil, nil, l, nil)
                        inuse l
                end
                if l.kind_of? Address and expr.type.integral?
                        l.modrm.imm = nil if l.modrm.imm and not l.modrm.imm.op and l.modrm.imm.rexpr == 0
                        if l.modrm.b and l.modrm.i and l.modrm.s == 1 and l.modrm.b.val == l.modrm.i.val
                                unuse l.modrm.b if l.modrm.b != l.modrm.i
                                l.modrm.b = nil
                                l.modrm.s = 2
                        end
                        case expr.op
                        when :+
                                rexpr = expr.rexpr
                                rexpr = rexpr.rexpr while rexpr.kind_of? C::CExpression and not rexpr.op and rexpr.type.integral? and
                                        rexpr.rexpr.kind_of? C::CExpression and rexpr.rexpr.type.integral? and
                                        typesize[rexpr.type.name] == typesize[rexpr.rexpr.type.name]
                                if rexpr.kind_of? C::CExpression and rexpr.op == :* and rexpr.lexpr
                                        r1 = c_cexpr_inner(rexpr.lexpr)
                                        r2 = c_cexpr_inner(rexpr.rexpr)
                                        r1, r2 = r2, r1 if r1.kind_of? Expression
                                        if r2.kind_of? Expression and [1, 2, 4, 8].include?(rr2 = r2.reduce)
                                                case r1
                                                when ModRM, Address, Reg
                                                        r1 = make_volatile(r1, rexpr.type) if not r1.kind_of? Reg
                                                        if not l.modrm.i or (l.modrm.i.val == r1.val and l.modrm.s == 1 and rr2 == 1)
                                                                unuse l, r1, r2
                                                                l = Address.new(l.modrm.dup)
                                                                inuse l
                                                                l.modrm.i = r1
                                                                l.modrm.s = (l.modrm.s || 0) + rr2
                                                                return l
                                                        end
                                                end
                                        end
                                        r = make_volatile(r1, rexpr.type)
                                        c_cexpr_inner_arith(r, :*, r2, rexpr.type)
                                else
                                        r = c_cexpr_inner(rexpr)
                                end
                                r = resolve_address r if r.kind_of? Address
                                r = make_volatile(r, rexpr.type) if r.kind_of? ModRM
                                case r
                                when Reg
                                        unuse l
                                        l = Address.new(l.modrm.dup)
                                        inuse l
                                        if l.modrm.b
                                                if not l.modrm.i or (l.modrm.i.val == r.val and l.modrm.s == 1)
                                                        l.modrm.i = r
                                                        l.modrm.s = (l.modrm.s || 0) + 1
                                                        unuse r
                                                        return l
                                                end
                                        else
                                                l.modrm.b = r
                                                unuse r
                                                return l
                                        end
                                when Expression
                                        unuse l, r
                                        l = Address.new(l.modrm.dup)
                                        inuse l
                                        l.modrm.imm = Expression[l.modrm.imm, :+, r]
                                        return l
                                end
                        when :-
                                r = c_cexpr_inner(expr.rexpr)
                                r = resolve_address r if r.kind_of? Address
                                if r.kind_of? Expression
                                        unuse l, r
                                        l = Address.new(l.modrm.dup)
                                        inuse l
                                        l.modrm.imm = Expression[l.modrm.imm, :-, r]
                                        return l
                                end
                        when :*
                                r = c_cexpr_inner(expr.rexpr)
                                if r.kind_of? Expression and [1, 2, 4, 8].includre?(rr = r.reduce)
                                        if l.modrm.b and not l.modrm.i
                                                if rr != 1
                                                        l.modrm.i = l.modrm.b
                                                        l.modrm.s = rr
                                                        l.modrm.imm = Expression[l.modrm.imm, :*, rr] if l.modrm.imm
                                                end
                                                unuse r
                                                return l
                                        elsif l.modrm.i and not l.modrm.b and l.modrm.s*rr <= 8
                                                l.modrm.s *= rr
                                                l.modrm.imm = Expression[l.modrm.imm, :*, rr] if l.modrm.imm and rr != 1
                                                unuse r
                                                return l
                                        end
                                end
                        end
                end
                l = make_volatile(l, expr.type) if l.kind_of? Address
                r ||= c_cexpr_inner(expr.rexpr)
                c_cexpr_inner_arith(l, expr.op, r, expr.type)
                l
        when :'='
                r = c_cexpr_inner(expr.rexpr)
                l = c_cexpr_inner(expr.lexpr)
                raise 'bad lvalue ' + l.inspect if not l.kind_of? ModRM and not @state.bound.index(l)
                r = resolve_address r if r.kind_of? Address
                r = make_volatile(r, expr.type) if l.kind_of? ModRM and r.kind_of? ModRM
                unuse r
                if expr.type.integral? or expr.type.pointer?
                        if r.kind_of? Address
                                m = r.modrm.dup
                                m.sz = l.sz
                                instr 'lea', l, m
                        else
                                if l.kind_of? ModRM and r.kind_of? Reg and l.sz != r.sz
                                        raise if l.sz > r.sz
                                        if l.sz == 8 and r.val >= 4
                                                reg = ([0, 1, 2, 3] - @state.used).first
                                                if not reg
                                                        rax = Reg.new(0, r.sz)
                                                        instr 'push', rax
                                                        instr 'mov', rax, r
                                                        instr 'mov', l, Reg.new(rax.val, 8)
                                                        instr 'pop', rax
                                                else
                                                        flushcachereg(reg)
                                                        instr 'mov', Reg.new(reg, r.sz), r
                                                        instr 'mov', l, Reg.new(reg, 8)
                                                end
                                        else
                                                instr 'mov', l, Reg.new(r.val, l.sz)
                                        end
                                elsif l.kind_of? ModRM and r.kind_of? Expression and l.sz == 64
                                        rval = r.reduce
                                        if !rval.kind_of?(Integer) or rval > 0xffff_ffff or rval < -0x8000_0000
                                                r = make_volatile(r, expr.type)
                                                unuse r
                                        end
                                        instr 'mov', l, r
                                else
                                        instr 'mov', l, r
                                end
                        end
                elsif expr.type.float?
                        raise 'float unhandled'
                end
                l
        when :>, :<, :>=, :<=, :==, :'!='
                l = c_cexpr_inner(expr.lexpr)
                l = make_volatile(l, expr.type)
                r = c_cexpr_inner(expr.rexpr)
                r = make_volatile(r, expr.type) if r.kind_of?(ModRM) and r.sz != l.sz
                unuse r
                if expr.lexpr.type.integral? or expr.lexpr.type.pointer?
                        instr 'cmp', l, i_to_i32(r)
                elsif expr.lexpr.type.float?
                        raise 'float unhandled'
                else raise 'bad comparison ' + expr.to_s
                end
                opcc = getcc(expr.op, expr.type)
                instr 'set'+opcc, Reg.new(l.val, 8)
                instr 'and', l, 1
                l
        else
                raise 'unhandled cexpr ' + expr.to_s
        end
end
c_cexpr_inner_nol(expr) click to toggle source

compile a CExpression with no lexpr

# File metasm/cpu/x86_64/compile_c.rb, line 303
def c_cexpr_inner_nol(expr)
        case expr.op
        when nil
                r = c_cexpr_inner(expr.rexpr)
                if (expr.rexpr.kind_of? C::CExpression or expr.rexpr.kind_of? C::Variable) and
                                expr.type.kind_of? C::BaseType and expr.rexpr.type.kind_of? C::BaseType
                        r = c_cexpr_inner_cast(expr, r)
                end
                r
        when :+
                c_cexpr_inner(expr.rexpr)
        when :-
                r = c_cexpr_inner(expr.rexpr)
                r = make_volatile(r, expr.type)
                if expr.type.integral? or expr.type.pointer?
                        instr 'neg', r
                elsif expr.type.float?
                        raise 'float unhandled'
                else raise
                end
                r
        when :'++', :'--'
                r = c_cexpr_inner(expr.rexpr)
                inc = true if expr.op == :'++'
                if expr.type.integral? or expr.type.pointer?
                        op = (inc ? 'inc' : 'dec')
                        instr op, r
                elsif expr.type.float?
                        raise 'float unhandled'
                end
                r
        when :&
                raise 'bad precompiler ' + expr.to_s if not expr.rexpr.kind_of? C::Variable
                @state.cache.each { |r_, c|
                        return inuse(r_) if c.kind_of? Address and c.target == expr.rexpr
                }
                r = c_cexpr_inner(expr.rexpr)
                raise 'bad lvalue' if not r.kind_of? ModRM
                unuse r
                r = Address.new(r)
                inuse r
                r.target = expr.rexpr
                r
        when :*
                expr.rexpr.type.name = :ptr if expr.rexpr.kind_of? C::CExpression and expr.rexpr.type.kind_of? C::BaseType and typesize[expr.rexpr.type.name] == typesize[:ptr]      # hint to use Address
                e = c_cexpr_inner(expr.rexpr)
                sz = 8*sizeof(expr)
                case e
                when Address
                        unuse e
                        e = e.modrm.dup
                        e.sz = sz
                        inuse e
                when ModRM; e = make_volatile(e, expr.rexpr.type)
                end
                case e
                when Reg; unuse e ; e = inuse ModRM.new(@cpusz, sz, nil, nil, e, nil)
                when Expression; e = inuse ModRM.new(@cpusz, sz, nil, nil, nil, e)
                end
                e
        when :'!'
                r = c_cexpr_inner(expr.rexpr)
                r = make_volatile(r, expr.rexpr.type)
                if expr.rexpr.type.integral? or expr.type.pointer?
                        r = make_volatile(r, expr.rexpr.type)
                        instr 'test', r, r
                elsif expr.rexpr.type.float?
                        raise 'float unhandled'
                else raise 'bad comparison ' + expr.to_s
                end
                instr 'setz', Reg.new(r.val, 8)
                instr 'and', r, Expression[1]
                r
        else raise 'mmh ? ' + expr.to_s
        end
end
c_decl(var) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 865
def c_decl(var)
        if var.type.kind_of? C::Array and
                        var.type.length.kind_of? C::CExpression
                reg = c_cexpr_inner(var.type.length)
                unuse reg
                instr 'sub', Reg.new(4, @cpusz), reg
                # TODO
        end
end
c_epilog() click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 1013
def c_epilog
        return if @state.func.attributes.to_a.include? 'naked'
        @state.dirty.reverse_each { |reg|
                instr 'pop', Reg.new(reg, @cpusz)
        }
        if ebp = @state.saved_rbp
                instr 'mov', Reg.new(4, ebp.sz), ebp
                instr 'pop', ebp
        elsif @state.args_space > 0 and @state.dirty.length & 1 == 0
                instr 'add', Reg.new(4, @cpusz), Expression[8]
        end
        instr 'ret'
end
c_goto(target) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 913
def c_goto(target)
        instr 'jmp', Expression[target]
end
c_ifgoto(expr, target) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 875
def c_ifgoto(expr, target)
        case o = expr.op
        when :<, :>, :<=, :>=, :==, :'!='
                l = c_cexpr_inner(expr.lexpr)
                r = c_cexpr_inner(expr.rexpr)
                r = make_volatile(r, expr.type) if r.kind_of? ModRM and l.kind_of? ModRM
                r = make_volatile(r, expr.type) if r.kind_of?(ModRM) and r.sz != l.sz
                l = make_volatile(l, expr.type) if l.kind_of?(ModRM) and r.kind_of?(Reg) and r.sz != l.sz
                if l.kind_of? Expression
                        o = { :< => :>, :> => :<, :>= => :<=, :<= => :>= }[o] || o
                        l, r = r, l
                end
                if expr.lexpr.type.integral? or expr.lexpr.type.pointer?
                        rr = i_to_i32(r)
                        rr = Reg.new(rr.val, l.sz) if rr.kind_of? Reg and rr.sz != l.sz     # XXX
                        instr 'cmp', l, rr
                elsif expr.lexpr.type.float?
                        raise 'float unhandled'
                else raise 'bad comparison ' + expr.to_s
                end
                unuse l, r
                op = 'j' + getcc(o, expr.lexpr.type)
                instr op, Expression[target]
        when :'!'
                r = c_cexpr_inner(expr.rexpr)
                r = make_volatile(r, expr.rexpr.type)
                unuse r
                instr 'test', r, r
                instr 'jz', Expression[target]
        else
                r = c_cexpr_inner(expr)
                r = make_volatile(r, expr.type)
                unuse r
                instr 'test', r, r
                instr 'jnz', Expression[target]
        end
end
c_init_state(func) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 940
def c_init_state(func)
        @state = State.new(func)
        args = func.type.args.dup
        if @parser.lexer.definition['__MS_X86_64_ABI__']
                @state.args_space = 32
                @state.regargs = [1, 2, 8, 9]
        else
                @state.args_space = 0
                @state.regargs = [7, 6, 2, 1, 8, 9]
        end
        c_reserve_stack(func.initializer)
        off = @state.offset.values.max.to_i
        off = 0 if off < 0

        argoff = 2*8 + @state.args_space
        rlist = @state.regargs.dup
        args.each { |a|
                if a.has_attribute_var('register')
                        off = c_reserve_stack_var(a, off)
                        @state.offset[a] = off
                elsif r = rlist.shift
                        if @state.args_space > 0
                                # use reserved space to spill regargs
                                off = -16-8*@state.regargs.index(r)
                        else
                                off = c_reserve_stack_var(a, off)
                        end
                        @state.offset[a] = off
                else
                        @state.offset[a] = -argoff
                        argoff = (argoff + sizeof(a) + 7) / 8 * 8
                end
        }
        if not @state.offset.values.grep(::Integer).empty?
                @state.saved_rbp = Reg.new(5, @cpusz)
                @state.used << 5
        end
end
c_label(name) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 917
def c_label(name)
        @state.cache.clear
        @source << '' << Label.new(name)
end
c_program_epilog() click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 1027
def c_program_epilog
end
c_prolog() click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 979
def c_prolog
        localspc = @state.offset.values.grep(::Integer).max
        return if @state.func.attributes.to_a.include? 'naked'
        @state.dirty -= @state.abi_trashregs
        if localspc
                localspc = (localspc + 7) / 8 * 8
                if @state.args_space > 0 and (localspc/8 + @state.dirty.length) & 1 == 1
                        # ensure 16-o stack align on windows
                        localspc += 8
                end
                ebp = @state.saved_rbp
                esp = Reg.new(4, ebp.sz)
                instr 'push', ebp
                instr 'mov', ebp, esp
                instr 'sub', esp, Expression[localspc] if localspc > 0

                rlist = @state.regargs.dup
                @state.func.type.args.each { |a|
                        if rn = a.has_attribute_var('register')
                                r = Reg.from_str(rn).val
                        elsif r = rlist.shift
                        else next
                        end
                        v = findvar(a)
                        instr 'mov', v, Reg.new(r, v.sz)
                }
        elsif @state.args_space > 0 and @state.dirty.length & 1 == 0
                instr 'sub', Reg.new(4, @cpusz), Expression[8]
        end
        @state.dirty.each { |reg|
                instr 'push', Reg.new(reg, @cpusz)
        }
end
c_return(expr) click to toggle source
# File metasm/cpu/x86_64/compile_c.rb, line 922
def c_return(expr)
        return if not expr
        @state.cache.delete_if { |r, v| r.kind_of? Reg and r.val == 0 and expr != v }
        r = c_cexpr_inner(expr)
        r = make_volatile(r, expr.type)
        unuse r
        instr 'mov', Reg.new(0, r.sz), r if r.val != 0
end
findreg(sz = @cpusz) click to toggle source

returns an available register, tries to find one not in @state.cache do not use with sz==8 (aliasing ah=>esp) does not put it in @state.inuse

# File metasm/cpu/x86_64/compile_c.rb, line 86
def findreg(sz = @cpusz)
        caching = @state.cache.keys.grep(Reg).map { |r| r.val }
        if not regval = (@state.abi_trashregs - @state.used - caching).first ||
                        ([*0..@regnummax] - @state.used).first
                raise 'need more registers! (or a better compiler?)'
        end
        getreg(regval, sz)
end
findvar(var) click to toggle source

returns a variable storage (ModRM for stack/global, Reg/Composite for register-bound)

# File metasm/cpu/x86_64/compile_c.rb, line 151
def findvar(var)
        if ret = @state.bound[var]
                return ret
        end

        if ret = @state.cache.index(var)
                ret = ret.dup
                inuse ret
                return ret
        end

        sz = 8*sizeof(var) rescue nil # extern char foo[];

        case off = @state.offset[var]
        when C::CExpression
                # stack, dynamic address
                # TODO
                # no need to update state.cache here, never recursive
                v = raise "find dynamic addr of #{var.name}"
        when ::Integer
                # stack
                # TODO -fomit-frame-pointer ( => state.cache dependant on stack_offset... )
                v = ModRM.new(@cpusz, sz, nil, nil, @state.saved_rbp, Expression[-off])
        when nil
                # global
                if @exeformat.cpu.generate_PIC
                        v = ModRM.new(@cpusz, sz, nil, nil, Reg.from_str('rip'), Expression[var.name, :-, '$_'])
                else
                        v = ModRM.new(@cpusz, sz, nil, nil, nil, Expression[var.name])
                end
        end

        case var.type
        when C::Array; inuse Address.new(v)
        else inuse v
        end
end
flushcachereg(regval) click to toggle source

remove the cache keys that depends on the register

# File metasm/cpu/x86_64/compile_c.rb, line 103
def flushcachereg(regval)
        @state.cache.delete_if { |e, val|
                case e
                when Reg; e.val == regval
                when Address; e = e.modrm ; redo
                when ModRM; e.b && (e.b.val == regval) or e.i && (e.i.val == regval)
                end
        }
end
getcc(op, type) click to toggle source

returns the instruction suffix for a comparison operator

# File metasm/cpu/x86_64/compile_c.rb, line 274
def getcc(op, type)
        case op
        when :'=='; 'z'
        when :'!='; 'nz'
        when :'<' ; 'b'
        when :'>' ; 'a'
        when :'<='; 'be'
        when :'>='; 'ae'
        else raise "bad comparison op #{op}"
        end.tr((type.specifier == :unsigned ? '' : 'ab'), 'gl')
end
getreg(regval, sz=@cpusz) click to toggle source

returns a Reg from a regval, mark it as dirty, flush old cache dependencies

# File metasm/cpu/x86_64/compile_c.rb, line 96
def getreg(regval, sz=@cpusz)
        flushcachereg(regval)
        @state.dirty |= [regval]
        Reg.new(regval, sz)
end
i_to_i32(v) click to toggle source

takes an argument, if the argument is an integer that does not fit in an i32, moves it to a temp reg the reg is unused, so use this only right when generating the offending instr (eg cmp, add..)

# File metasm/cpu/x86_64/compile_c.rb, line 259
def i_to_i32(v)
        if v.kind_of? Expression and i = v.reduce and i.kind_of?(Integer)
                i &= 0xffff_ffff_ffff_ffff
                if i <= 0x7fff_ffff
                elsif i >= (1<<64)-0x8000_0000
                        v = Expression[Expression.make_signed(i, 64)]
                else
                        v = make_volatile(v, C::BaseType.new(:int))
                        unuse v
                end
        end
        v
end
instr(name, *args) click to toggle source

shortcut to add an instruction to the source

# File metasm/cpu/x86_64/compile_c.rb, line 78
def instr(name, *args)
        # XXX parse_postfix ?
        @source << Instruction.new(@exeformat.cpu, name, args)
end
inuse(v) click to toggle source

marks an arg as in use, returns the arg

# File metasm/cpu/x86_64/compile_c.rb, line 137
def inuse(v)
        case v
        when Reg; @state.used |= [v.val]
        when ModRM
                @state.used |= [v.i.val] if v.i
                @state.used |= [v.b.val] if v.b
        when Address; inuse v.modrm ; return v
        else return v
        end
        @state.inuse |= [v]
        v
end
make_volatile(e, type, rsz=@cpusz) click to toggle source

copies the arg e to a volatile location (register/composite) if it is not already unuses the old storage may return a register bigger than the type size (eg __int8 are stored in full reg size)

# File metasm/cpu/x86_64/compile_c.rb, line 212
def make_volatile(e, type, rsz=@cpusz)
        if e.kind_of? ModRM or @state.bound.index(e)
                if type.integral? or type.pointer?
                        oldval = @state.cache[e]
                        unuse e
                        sz = typesize[type.pointer? ? :ptr : type.name]*8
                        if sz < @cpusz or sz < rsz or e.sz < rsz
                                e2 = inuse findreg(rsz)
                                op = ((type.specifier == :unsigned) ? 'movzx' : 'movsx')
                                op = 'mov' if e.sz == e2.sz
                                if e2.sz == 64 and e.sz == 32
                                        if op == 'movsx'
                                                instr 'movsxd', e2, e
                                        else
                                                instr 'mov', Reg.new(e2.val, 32), e
                                        end
                                else
                                        instr op, e2, e
                                end
                        else
                                e2 = inuse findreg(sz)
                                instr 'mov', e2, e
                        end
                        @state.cache[e2] = oldval if oldval and e.kind_of? ModRM
                        e2
                elsif type.float?
                        raise 'float unhandled'
                else raise "cannot cast #{e} to #{type}"
                end
        elsif e.kind_of? Address
                make_volatile resolve_address(e), type, rsz
        elsif e.kind_of? Expression
                if type.integral? or type.pointer?
                        e2 = inuse findreg
                        instr 'mov', e2, e
                        e2
                elsif type.float?
                        raise 'float unhandled'
                else raise "cannot cast #{e} to #{type}"
                end
        else
                e
        end
end
resolve_address(e) click to toggle source

resolves the Address to Reg/Expr (may encode an 'lea')

# File metasm/cpu/x86_64/compile_c.rb, line 190
def resolve_address(e)
        r = e.modrm
        unuse e
        if r.imm and not r.b and not r.i
                reg = r.imm
        elsif not r.imm and ((not r.b and r.s == 1) or not r.i)
                reg = r.b || r.i
        elsif reg = @state.cache.index(e)
                reg = reg.dup
        else
                reg = findreg
                r.sz = reg.sz
                instr 'lea', reg, r
        end
        inuse reg
        @state.cache[reg] = e
        reg
end
unuse(*vals) click to toggle source

removes elements from @state.inuse, free @state.used if unreferenced must be the exact object present in inuse

# File metasm/cpu/x86_64/compile_c.rb, line 115
def unuse(*vals)
        vals.each { |val|
                val = val.modrm if val.kind_of? Address
                @state.inuse.delete val
        }
        # XXX cache exempt
        exempt = @state.bound.values.map { |r| r.val }
        exempt << 4
        exempt << 5 if @state.saved_rbp
        @state.used.delete_if { |regval|
                next if exempt.include? regval
                not @state.inuse.find { |val|
                        case val
                        when Reg; val.val == regval
                        when ModRM; (val.b and val.b.val == regval) or (val.i and val.i.val == regval)
                        else raise 'internal error - inuse ' + val.inspect
                        end
                }
        }
end