문제

C/C ++ 프레임 워크 내에서 동적 코드 생성의 기만적으로 간단한 기초가 이미 다루었습니다. 다른 질문. 코드 예제와 함께 주제에 부드러운 소개가 있습니까?

내 필요가 훨씬 더 겸손 할 때 내 눈이 매우 복잡한 오픈 소스 JIT 컴파일러를 쳐다 보면서 피가 나기 시작합니다.

컴퓨터 과학 박사 학위를 취득하지 않는 주제에 대한 좋은 텍스트가 있습니까? 나는 잘 마모 된 패턴, 조심해야 할 것들, 성능 고려 사항 등을 찾고 있습니다. 전자 또는 트리 기반 자원은 똑같이 가치가있을 수 있습니다. (x86이 아니라) 어셈블리 언어에 대한 실무 지식을 가정 할 수 있습니다.

도움이 되었습니까?

해결책

에뮬레이터에 사용한 패턴은 다음과 같은 일입니다.

typedef void (*code_ptr)();
unsigned long instruction_pointer = entry_point;
std::map<unsigned long, code_ptr> code_map;


void execute_block() {
    code_ptr f;
    std::map<unsigned long, void *>::iterator it = code_map.find(instruction_pointer);
    if(it != code_map.end()) {
        f = it->second
    } else {
        f = generate_code_block();
        code_map[instruction_pointer] = f;
    }
    f();
    instruction_pointer = update_instruction_pointer();
}

void execute() {
    while(true) {
        execute_block();
    }
}

이것은 단순화이지만 아이디어가 있습니다. 기본적으로 엔진이 "기본 블록"(일반적으로 다음 플로우 제어 OP 또는 전체 기능까지 모든 기능)을 실행하도록 요청할 때마다 이미 생성되었는지 확인합니다. 그렇다면 실행하고, 그렇지 않으면 생성하고 추가 한 다음 실행하십시오.

헹굼 반복 :)

코드 생성에 관해서는 약간 복잡해 지지만 아이디어는 VM의 맥락에서 기본 블록의 작업을 수행하는 적절한 "기능"을 방출하는 것입니다.

편집 : 최적화도 시연하지 않았지만 "온화한 소개"를 요청했습니다.

편집 2 :이 패턴으로 구현할 수있는 가장 즉시 생산적인 속도 업 중 하나를 언급하는 것을 잊었습니다. 기본적으로, 당신은 절대 나무에서 블록을 제거하십시오 (하지 않으면 작업을 수행 할 수는 있지만 절대하지 않으면 더 간단합니다). 그러면 조회를 피하기 위해 "체인"을 함께 "체인"할 수 있습니다. 여기 개념이 있습니다. F ()에서 돌아와서 "update_instruction_pointer"를 수행하려고 할 때마다 방금 실행 된 블록이 통화, 무조건 점프 또는 유량 제어로 끝나지 않은 경우 "Fixup"을 "고정"할 수 있습니다. 다음 블록에 직접 JMP가있는 RET 명령은 실행됩니다 (항상 동일하므로) 만약에 당신은 이미 그것을 방출했습니다. 이로 인해 VM에서 점점 더 자주 실행되고 "execute_block"기능에서 점점 더 적고 적습니다.

다른 팁

JIT와 관련된 소스는 알지 못하지만 일반 컴파일러와 거의 비슷하다고 생각합니다. 성능에 대해 걱정하지 않으면 더 간단합니다.

가장 쉬운 방법은 VM 통역사로 시작하는 것입니다. 그런 다음 각 VM 명령에 대해 통역사가 실행 한 어셈블리 코드를 생성하십시오.

그 이상으로 VM 바이트 코드를 구문 분석하고 적합한 중간 양식 (3 개의 주소 코드? SSA?)으로 변환 한 다음 다른 컴파일러에서와 같이 코드를 최적화하고 생성 할 것이라고 생각합니다.

스택 기반 VM의 경우 바이트 코드를 중간 형태로 변환 할 때 "현재"스택 깊이를 추적하고 각 스택 위치를 변수로 취급하는 데 도움이 될 수 있습니다. 예를 들어, 현재 스택 깊이가 4라고 생각하고 "푸시"명령이 표시되면 "stack_variable_5"에 할당을 생성하고 컴파일 타임 스택 카운터 또는 그와 비슷한 것을 증가시킬 수 있습니다. 스택 깊이가 5 인 경우 "추가"는 코드를 생성 할 수 있으며 "stack_variable_4 = stack_variable_4+stack_variable_5"를 생성하고 컴파일 타임 스택 카운터를 줄입니다.

스택 기반 코드를 구문 트리로 변환 할 수도 있습니다. 컴파일 타임 스택을 유지하십시오. 모든 "푸시"명령은 푸시가 스택에 저장되도록 표현합니다. 연산자는 피연산자를 포함하는 구문 트리 노드를 만듭니다. 예를 들어, "xy +"는 스택에 "var (x)", "var (x) var (y)"를 포함하게 만들 수 있습니다. 그리고 플러스는 var 참조를 모두 팝하고 "플러스 (var (x), 달라지다))".

로터에 관한 Joel Pobar의 책 사본을 얻고 (외출 할 때) 소스를 통해 sscli. 조심하십시오, 광기는 안에 있습니다 :)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top