Polymorphism Without Indirect Jumps?

When I learned MIPS assembly a few months ago, a question popped into my head that I forgot to ask then, so I thought I'd ask it now:

Is it possible to implement polymorphism without indirect jump instructions? If so, how?

(Indirect jumps are the "jump register" instructions, eg jr $t0 in MIPS or jmp EAX in x86.)

One such solution I came up with is self-modifying code, but I'm curious as to whether polymorphism (and so OOP) would be possible by any other means.


The simplest answer to your question would be to write your program (and possible assembler) in such a way that all method calls can be resolved at runtime thus negating the need for a lookup table. I'm going to assume you're talking about passing a subclass to a function that was designed for a superclass and it is therefore impossible to implement this optimization.

Eliminating the lookup, I think, is outside the scope of your question so I'm going to suggest replacements for the jmp <reg> instruction (sorry, I only know x86).

  • You can execute a call <mem> on a memory address (isn't this how you would do it using a lookup table?)
  • You can execute a call <reg> on a register (not entirely different from the jmp , but does answer your question)
  • You can jmp <mem> if you wanted, but that's not all that different from jmp <reg>
  • All of these are possible and solve your problem, but are all alike. I guess this illustrates my confusion on why you would want to do what you're asking. You have to have some way to choose which method to call (the vtable) and some way to transfer execution to the method (using jmp or call ).

    The only other possible way to do this would be to fiddle with the register that is used to point to the next command in the execution chain ( EIP in x86). Whether you can or should is another question. I suppose if you were intimately knowledgeable of the architecture and weren't worried about it changing you could do that.


    Yes

    You may want to consider changing your question however

    Consider this C++ code where Derived is obviously polymorphic (yes, I didn't delete the object):

    class Base
    {
        public: int foo(){ return 1; }
    };
    
    class Derived: public Base
    {
        public: int foo(){ return 2; };
    };
    
    int main()
    {
        Base* b = new Derived();
    
        return b->foo();    //Returns 1
    }
    

    The assembly generated by gcc is

    main:
    .LFB2:
        pushq   %rbp
        .seh_pushreg    %rbp
        movq    %rsp, %rbp
        .seh_setframe   %rbp, 0
        subq    $48, %rsp
        .seh_stackalloc 48
        .seh_endprologue
        call    __main
        movl    $1, %ecx
        call    _Znwm
        movq    %rax, -8(%rbp)
        movq    -8(%rbp), %rax
        movq    %rax, %rcx
        call    _ZN4Base3fooEv
        addq    $48, %rsp
        popq    %rbp
        ret
    

    As you can see there is no indirect jump/call.

    That's because polymorphism is not the point here (though it is necessary), virtual methods are.

    Then the answer becames

    No

    If by indirect jump/call you means every technique that use a runtime value for computing the target of the jump/call (so including things like ret , call [] , call reg , jr , jalr ).

    Consider this source

    #include <iostream>
    
    class Base
    {
        public: virtual int foo(){ return 1; }
    };
    
    class Derived: public Base
    {
        public: int foo(){ return 2; };
    };
    
    class Derived2: public Base
    {
        public: int foo(){ return 3; };
    };
    
    
    
    int main()
    {
        int a;
        Base* b = 0; //don't remember the header for std::nullptr right now...
    
        std::cin >> a;
    
        if (a > 241)
            b = new Derived();
        else 
            b = new Derived2();
    
        return b->foo();    //Returns what?
    }
    

    The result depends on the user input, an archetypal runtime value, so must be the called routine and no static address would do.

    Note that in this case the compiler could use a jump toward calls with static address (as either Derived2::foo or Derived::foo is called) but this is not in general possible (you may have multiple object files without the source, you may have an aliasing pointer, the pointer b could be set by an external library, and so on).

    链接地址: http://www.djcxy.com/p/67124.html

    上一篇: “如何”在Backbone.js中保存整个集合

    下一篇: 没有间接跳跃的多态性?