레지스터 할당

Register allocation

컴파일러 최적화에서 레지스터 할당은 제한된 수의 프로세서 레지스터에 로컬 자동 변수와 식 결과를 할당하는 프로세스입니다.

레지스터 할당은 기본 블록(로컬 레지스터 할당), 함수/프로시저 전체(글로벌 레지스터 할당) 또는 콜그래프를 통해 횡단된 함수 경계(프로시저레지스터 할당)에서 발생할 수 있습니다.함수/프로시저별로 실행되면 호출 규약에 따라콜사이트 주위에 저장/복원 삽입이 필요할 수 있습니다.

맥락

원칙

가장 일반적인 아키텍처에서 다른 수의 스칼라 레지스터
아키텍처 32비트 64비트
15 31
인텔 x86 8 16
MIPS 32 32
전원/전원PC 32 32
RISC-V 16/32 32
SPARC 31 31


많은 프로그래밍 언어에서 프로그래머는 임의의 수의 변수를 사용할 수 있습니다.컴퓨터는 CPU의 레지스터를 빠르게 읽고 수 있기 때문에 CPU의 [1]레지스터에 더 많은 변수가 있을 때 컴퓨터 프로그램이 더 빨리 실행된다.또, 경우에 따라서는, 레지스터에 액세스 하는 코드가 보다 콤팩트하기 때문에, 코드의 사이즈가 작아져, 메모리보다 레지스터를 사용하면, 보다 고속으로 취득할 수 있습니다.다만, 레지스터의 수는 한정되어 있습니다.따라서 컴파일러는 코드를 기계어로 변환할 때 CPU [2][3]내의 제한된 수의 레지스터에 변수를 할당하는 방법을 결정해야 합니다.

모든 변수가 동시에 사용(또는 "라이브")되는 은 아니기 때문에 프로그램의 수명 동안 주어진 레지스터를 사용하여 다른 변수를 보유할 수 있습니다.그러나 동시에 사용 중인 두 변수를 동일한 레지스터에 할당하려면 변수 중 하나가 손상되어야 합니다.모든 변수를 저장할 수 있는 레지스터가 충분하지 않은 경우 일부 변수는 RAM으로 이동하거나 RAM에서 이동할 수 있습니다.이 프로세스를 레지스터의 「[4]스필링」이라고 부릅니다.프로그램의 수명 동안 변수는 유출되거나 레지스터에 저장될 수 있습니다. 이 변수는 "분할"[5]로 간주됩니다.RAM에 액세스하는 것은 레지스터에 액세스하는 것보다 훨씬 느리기 때문에 컴파일된 프로그램의 실행 속도가 느립니다.따라서 최적화 컴파일러는 가능한 한 많은 변수를 레지스터에 할당하는 것을 목표로 합니다.높은 "등록 압력"은 더 많은 유출과 재장전이 필요하다는 것을 의미하는 기술 용어이며, Braun 등에 의해 "명령에서 동시에 활성 변수의 수"[7]로 정의됩니다.

또한 일부 컴퓨터 설계는 자주 액세스하는 레지스터를 캐시합니다.따라서 프로그램은 같은 레지스터를 소스와 수신처에 할당함으로써 더욱 최적화될 수 있습니다.move가능한 한 지시해 주세요.이는 컴파일러가 정적 Single-Assignment Form(SSA; 단일 할당 형식) 등의 중간 표현을 사용하는 경우 특히 중요합니다.특히 SSA가 완전히 최적화되지 않은 경우 인위적으로 추가 생성될 수 있습니다.move지침들.

레지스터 할당 구성 요소

따라서 레지스터 할당은 런타임에 변수를 저장할 위치(즉 레지스터 내부 또는 외부)를 선택하는 것으로 구성됩니다.변수가 레지스터에 저장될 경우 할당자는 이 변수가 저장될 레지스터를 결정해야 합니다.결국 변수가 같은 장소에 머무는 기간을 결정하는 것도 과제입니다.

레지스터 할당자는 선택한 할당 전략을 무시하고 일련의 핵심 액션에 의존하여 이러한 과제를 해결할 수 있습니다.이러한 액션은, 몇개의 [8]다른 카테고리로 수집할 수 있습니다.

삽입 이동
이 동작은 레지스터 간의 이동 명령 수를 증가시키는 것으로 구성됩니다. 즉, 변수가 수명 동안 하나의 레지스터가 아닌 다른 레지스터에 존재하도록 합니다.이 문제는 라이브 범위 분할 접근법에서 발생합니다.
흘리다
이 동작은 [9]레지스터가 아닌 메모리에 변수를 저장하는 것으로 구성됩니다.
과제
이 액션은 레지스터를 [10]변수에 할당하는 것으로 구성됩니다.
통합
이 액션은 레지스터 간의 이동 수를 제한하여 명령의 총 수를 제한합니다.예를 들어, 다양한 메서드에 걸쳐 라이브로 변수를 식별하고 전체 [9]수명 동안 하나의 레지스터에 저장합니다.

많은 레지스터 할당 방식은 하나 이상의 특정 범주의 작업에 최적화됩니다.

Intel 386 레지스터

레지스터 할당에서 자주 발생하는 문제

레지스터 할당은 다양한 레지스터 할당 접근법에 의해 해결(또는 회피)될 수 있는 몇 가지 문제를 일으킵니다.가장 일반적인 문제의 3가지를 다음에 나타냅니다.

앨리어싱
아키텍처에 따라서는 어떤 레지스터에 값을 할당하면 다른 레지스터의 값에 영향을 줄 수 있습니다.이것을 에일리어스라고 부릅니다.예를 들어 x86 아키텍처에는 4개의 범용 32비트 레지스터가 있으며 16비트 [11]또는 8비트 레지스터로도 사용할 수 있습니다.이 경우 eax 레지스터에 32비트 값을 할당하면 al 레지스터의 값에 영향을 미칩니다.
프리컬러링
이 문제는 일부 변수를 특정 레지스터에 강제로 할당하는 행위입니다.예를 들어, Power에서PC규칙, 파라미터는 일반적으로 R3-R10으로 전달되며 반환값은 R3으로 전달됩니다.[12]
NP 문제
Chaitin 등은 레지스터 할당이 NP-완전한 문제임을 보여주었다.그들은 임의의 그래프에 대해 프로그램에 대한 레지스터 할당(노드를 나타내는 레지스터와 사용 가능한 색상을 나타내는 머신 레지스터를 포함)이 원래 그래프에 대한 색상이 되도록 프로그램을 구성할 수 있다는 것을 보여줌으로써 그래프 색칠 문제를 레지스터 할당 문제로 줄인다.Graph Coloring은 NP-Hard 문제이고 Register Allocation은 NP이기 때문에 [13]이는 문제의 NP 완전성을 증명합니다.

할당 기술 등록

레지스터 할당은 기본 코드 블록에 걸쳐 발생할 수 있다: "로컬"이라고 하며,[14] Horwitz 등에 의해 처음 언급되었다.기본 블록에는 브런치가 포함되어 있지 않기 때문에 레지스터 할당에서의 제어 흐름 그래프 머지 포인트의 관리에는 시간이 걸리는 [15]조작이[clarification needed] 나타나기 때문에 할당 프로세스는 고속이라고 생각됩니다.그러나 이 접근법은 전체 컴파일 유닛(예를 들어 [16]방법 또는 절차)에서 작동하는 "글로벌" 접근법만큼 최적화된 코드를 생성하지 못할 것으로 생각된다.

그래프 색상 할당

그래프 색상 할당은 레지스터 [17][18]할당을 해결하기 위한 주요 접근 방식입니다.그것은 Chaitin [4]등에 의해 처음 제안되었다.이 접근법에서는 그래프 내의 노드는 레지스터 할당 후보인 라이브 범위(변수, 임시, 가상/심볼릭레지스터)를 나타냅니다.에지는 간섭하는 라이브 범위, 즉 하나 이상의 프로그램 포인트에서 동시에 라이브인 라이브 범위를 연결합니다.그 후 레지스터 할당은 에지로 연결된 두 노드가 동일한 [19]색상을 받지 않도록 노드에 색상(레지스터)이 할당되는 그래프 색칠 문제로 감소합니다.

라이브스 분석을 사용하여 간섭 그래프를 작성할 수 있습니다.노드가 프로그램의 변수인 무방향 그래프인 간섭 그래프는 동일한 [20]레지스터에 할당할 수 없는 변수를 모델링하는 데 사용됩니다.

원칙

Chaitin 스타일의 그래프 색상 레지스터 할당기의 주요 단계는 다음과 같습니다.[18]

Chaitin 등의 반복 그래프 색칠 기반 레지스터 할당자
  1. Renumber: 소스 프로그램에서 라이브 범위 정보를 검색합니다.
  2. 빌드: 간섭 그래프를 빌드합니다.
  3. 병합: 복사 명령과 관련된 비간섭 변수의 라이브 범위를 병합합니다.
  4. 유출 비용: 각 변수의 유출 비용을 계산합니다.이것은 변수를 메모리에 매핑하는 것이 최종 프로그램의 속도에 미치는 영향을 평가합니다.
  5. 심플화: 추론 그래프에 노드의 순서를 작성합니다.
  6. 스필 코드: 스필 명령을 삽입합니다. 즉, 레지스터와 메모리 사이의 값을 이동시키기 위해 로드 및 저장하십시오.
  7. : 각 변수에 레지스터를 할당합니다.

단점 및 추가 개선점

그래프 색상 할당에는 세 가지 주요 단점이 있습니다.우선, 어떤 변수가 유출될지를 결정하기 위해 NP-완전 문제인 그래프 색칠에 의존합니다.최소 색채 그래프를 찾는 것은 실제로 NP-완전한 문제입니다.[21]둘째, 라이브 레인지 분할을 사용하지 않는 한 제거된 변수는 모든 곳에 유출됩니다. 저장(각각 로드) 명령은 가능한 한 빨리(각각 늦게) 삽입됩니다. 즉, (각각 사용 전) 변수 정의 직후에 삽입됩니다.셋째, 유출되지 않은 변수는 수명 [22]내내 동일한 레지스터에 보관됩니다.

한편, 1개의 레지스터명은 복수의 레지스터 클래스에 표시되는 경우가 있습니다.여기서 클래스는 특정 역할로 교환 가능한 레지스터 이름의 세트입니다.그 후 여러 레지스터 이름이 [23]단일 하드웨어 레지스터의 에일리어스일 수 있습니다.마지막으로, 그래프 색칠은 레지스터를 할당하기 위한 공격적인 기술이지만, 라이브 [24]범위 수에서 2차인 최악의 경우 크기를 가질 수 있는 간섭 그래프를 사용하기 때문에 계산 비용이 많이 듭니다.그래프 컬러 레지스터 할당의 전통적인 공식은 중복되지 않는 범용 레지스터의 단일 뱅크를 암시적으로 가정하며 중복 레지스터 쌍, 특수 목적 레지스터 및 다중 레지스터 [25]뱅크와 같은 불규칙한 아키텍처 특징을 처리하지 않습니다.

브릭스 외 연구진은 차이틴 스타일의 그래프 컬러링 접근법의 이후 개선점을 발견했다.: 이것은 보수적 결합이라고 불립니다.이 개선으로 두 라이브 범위를 병합할 수 있는 시기를 결정하는 기준이 추가됩니다.주로, 비간섭 요구사항 외에도, 두 변수가 병합되어 추가 유출이 발생하지 않는 경우에만 병합할 수 있다.Briggs et al.는 Chaitin의 작품에 편향된 색채의 두 번째 개선점을 도입했다.편향된 색상은 그래프 색상의 동일한 색상을 복사와 [18]관련된 라이브 범위에 할당하려고 합니다.

선형 스캔

선형 스캔은 또 다른 글로벌 레지스터 할당 방법입니다.1999년 [26]폴레토 등에 의해 처음 제안되었다.이 방법에서는 코드는 그래프로 변환되지 않습니다.대신 모든 변수가 선형 스캔되어 라이브 범위를 결정합니다(구간으로 표시됨).모든 변수의 활성 범위를 파악한 후, 간격은 시간순으로 횡단됩니다.이 트래버설은 라이브 범위가 간섭하는 변수를 식별하는 데 도움이 될 수 있지만 간섭 그래프는 작성되지 않고 변수가 과도한 방식으로 [24]할당됩니다.

이 접근법의 동기는 속도입니다. 생성된 코드의 실행 시간이 아니라 코드 생성에 소요된 시간 측면에서입니다.일반적으로 표준 그래프 컬러링 접근법은 품질 코드를 생성하지만 2차 [29]비용을 갖는 사용된 그래프 컬러링 알고리즘의 상당[27][28]오버헤드가 있다.이 기능으로 인해 리니어 스캔은 현재 Hotspot 클라이언트 컴파일러, V8Jikes RVM과 [5]같은 여러 JIT 컴파일러에서 사용되는 접근 방식입니다.Hotspot 서버 컴파일러는 상위 코드에 그래프 색상을 사용합니다.[30]

유사 코드

이것은 Poletto [31]등이 최초로 제안한 알고리즘에 대해 설명합니다.

  • R은 사용 가능한 레지스터의 수입니다.
  • active는 현재 포인트와 겹치는 라이브인터벌의 리스트로 엔드 포인트 증가순으로 정렬되어 레지스터에 배치됩니다.
Linear Scan Register(선형 스캔 레지스터) 라이브 간격 i에 대해 할당 활성 ← {}, 길이(액티브) = R이면 시작 지점을 늘리는 순서대로 ExpireOldIntervals(i)를 수행한 다음 SpillAt를 수행합니다.간격(i) else register[i] ← 활성 상태의 각 간격 j대해 끝점 ExpireOldIntervals(i)를 늘려 정렬된 빈 레지스터 풀에서 제거된 레지스터를 활성에 추가합니다. 끝점 j가 [j] point 시작점[i]인 경우 해당 레지스터에서 제거 j를 po로 반환합니다.프리 레지스터의 spillAt ol끝점[interval] > 끝점[i]인 경우 간격(i) 스필 ← 활성 상태에서의 마지막 간격[i] ← 등록 [interval] 위치[interval] ← 활성 상태 추가 i에서 활성 상태로의 스필 제거(다른 끝점 위치[i] ← 새 스택 위치)

단점 및 추가 개선점

그러나 선형 스캔에는 두 가지 주요 단점이 있습니다.첫째, 욕심 많은 측면 때문에 평생의 구멍, 즉 "변수 값이 필요하지 않은 범위"[32][33]를 고려하지 않습니다.또한 유출된 변수는 평생 유출된 상태로 유지됩니다.

SSA 어프로치에 의한 라이브 레인지의 단축

폴레토의 선형 스캔 알고리즘에 대한 많은 다른 연구들이 뒤따랐다.예를 들어, Traub 등은 더 나은 [34][35]품질의 코드를 생성하는 것을 목표로 하는 second-chance binpacking이라고 불리는 알고리즘을 제안했다.이 접근법에서는 유출된 변수는 표준 선형 스캔 알고리즘에서 사용되는 것과 다른 휴리스틱을 사용하여 나중에 레지스터에 저장할 수 있습니다.알고리즘은 라이브 간격을 사용하는 대신 라이브 범위를 사용합니다.즉, 범위를 흘려야 할 경우 이 변수에 대응하는 다른 범위를 모두 흘릴 필요는 없습니다.

또한 선형 스캔 할당은 SSA 형식을 활용하도록 조정되었습니다. 이 중간 표현의 속성은 할당 알고리즘을 단순화하고 수명 [36]홀을 직접 계산할 수 있도록 합니다.첫째, 수명 간격을 구축하기 위한 데이터 흐름 그래프 분석에 소요되는 시간이 단축됩니다. 즉, 변수가 [37]고유하기 때문입니다.따라서 새로운 할당이 새로운 라이브 [38][39]간격에 대응하므로 라이브 간격이 짧아집니다.모델링 간격과 활성 구멍을 피하기 위해 Rogers는 [40]명령의 80%에 대한 간격을 성공적으로 제거하는 future-active 집합이라고 불리는 단순화를 보여주었습니다.

재물질화

최적의 레지스터 할당 문제는 NP-complete입니다.그 결과 컴파일러는 휴리스틱 기법을 사용하여 솔루션을 근사합니다.

Chaitin 등은 스필 코드의 품질을 개선하기 위한 몇 가지 아이디어를 논의한다.그들은 특정 값은 하나의 명령으로 재계산할 수 있으며 필요한 피연산자는 항상 계산에 사용할 수 있다고 지적한다.이들은 이러한 예외값을 "never-kill"이라고 부르며 이러한 값을 흘려보내고 새로고침하지 말고 다시 계산해야 한다는 점에 유의합니다.또한, 원하는 레지스터에 [41]직접 재계산함으로써 절대 삭제되지 않은 값의 비콜리지를 제거할 수 있다는 점에 유의하십시오.

이러한 기술을 재물질화라고 합니다.실무상 재실현 기회는 다음과 같다.

  • 정수 상수의 즉시 부하 및 일부 기계에서는 부동소수점 상수
  • 프레임 포인터 또는 정적 데이터 영역에서 일정한 오프셋을 계산합니다.
  • 디스플레이에서 [41]로컬이 아닌 프레임 포인터를 로드합니다.

브릭스 등Chaitin의 작업을 확장하여 복잡하고 다치화된 라이브 범위를 위한 재실현 기회를 활용합니다.이들은 각 값에 할당자가 올바르게 처리할 수 있도록 충분한 정보를 태그 붙여야 한다는 것을 발견했습니다.Briggs의 접근방식은 다음과 같다. 먼저 각 활성 범위를 성분 값으로 분할한 후 각 값에 재물질화 태그를 전파하고 동일한 [41]태그를 가진 연결된 값에서 새로운 활성 범위를 형성한다.

통합

레지스터 할당의 맥락에서 병합은 변수에서 변수로의 이동 작업을 병합하는 작업입니다. 이러한 두 변수를 동일한 위치에 할당함으로써 이 작업을 병합하는 작업입니다.간섭 그래프가 작성된 후 병합 작업이 수행됩니다.두 노드가 병합되면 복사 작업이 [42]불필요해지면 동일한 색상을 가져와 동일한 레지스터에 할당해야 합니다.

병합을 수행하면 간섭 그래프의 [9]색상에 긍정적인 영향과 부정적인 영향을 모두 미칠 수 있습니다.예를 들어, 병합이 그래프 추론 색채성에 미칠 수 있는 부정적인 영향 중 하나는 두 노드가 병합되는 경우이며, 결과 노드는 [9]병합되는 에지 결합을 가집니다.추론 그래프 색채성에 대한 결합의 긍정적인 영향은 예를 들어 노드가 결합되는 두 노드를 간섭할 때 노드의 정도가 하나 감소하여 간섭 [43]그래프의 전체 색채성을 향상시킨다.

몇 가지 통합 휴리스틱을 사용할 [44]수 있습니다.

적극적인 통합
Chaitin 오리지널 레지스터 할당자에 의해 처음 소개되었습니다.이 경험적 접근법은 간섭이 없는 복사 관련 [45]노드를 병합하는 것을 목적으로 합니다.카피 제거의 관점에서는, 이 휴리스틱이 최고의 [46]결과를 가져옵니다.반면에, 공격적인 결합은 추론 그래프의 [43]색채화에 영향을 미칠 수 있다.
보수당 통합
주로 적극적인 병합과 동일한 경험적 접근법을 사용하지만 간섭 [47]그래프의 색채를 손상시키지 않는 경우에만 이동을 병합합니다.
반복된 병합
그래프의 [48]색채성을 유지하면서 한 번에 하나의 특정 이동을 제거합니다.
최적의 통합
공격적인 결합을 기반으로 하지만 추론 그래프 색채가 손상되면 가능한 [49]한 적은 수의 이동을 포기합니다.

혼재된 접근법

하이브리드 할당

일부 다른 레지스터 할당 접근법은 레지스터 사용을 최적화하기 위한 하나의 기술로 제한되지 않습니다.예를 들어 Cavazos et.al은 선형 스캔과 그래프 컬러링 알고리즘을 [50]모두 사용할 수 있는 솔루션을 제안했습니다.이 접근방식에서는 어느 하나의 솔루션과 다른 솔루션 중 하나를 동적으로 결정합니다.첫째, 머신러닝 알고리즘은 "오프라인"으로, 즉 런타임에 사용되지 않는 것으로, 어느 할당 알고리즘을 사용할 필요가 있는지를 결정하는 휴리스틱 함수를 구축하기 위해서 사용됩니다.그런 다음 경험적 함수는 런타임에 사용됩니다. 코드 동작에 따라 할당자는 사용 가능한 [51]두 알고리즘 중 하나를 선택할 수 있습니다.

트레이스 레지스터 할당은 Eisl 등에 의해 개발된 최신 접근법입니다.[3][5]이 기술은 할당을 로컬로 처리합니다.이 기술은 동적 프로파일링 데이터에 의존하여 특정 제어 흐름 그래프에서 가장 자주 사용되는 브랜치를 결정합니다.그런 다음 가장 많이 사용되는 분기에 대해 병합 지점이 무시되는 일련의 "추적"(즉, 코드 세그먼트)을 추론합니다.그런 다음 각 트레이스는 할당자에 의해 독립적으로 처리됩니다.다른 [52]트레이스간에 다른 레지스터 할당 알고리즘을 사용할 수 있기 때문에, 이 어프로치는 하이브리드로 간주할 수 있습니다.

분할 할당

분할 할당은 다른 접근 방식을 조합하는 또 다른 레지스터 할당 기법이며, 일반적으로는 반대라고 간주됩니다.예를 들어 첫 번째 휴리스틱 구축 단계가 오프라인으로 실행되고 휴리스틱 사용이 온라인으로 이루어지기 [24]때문에 하이브리드 할당 기술은 분할된 것으로 간주할 수 있다.같은 방법으로 B.Diouf 등은 오프라인 및 온라인 동작, 즉 정적 및 동적 [53][54]컴파일에 의존하는 할당 기술을 제안했다.오프라인 단계에서는 먼저 정수 선형 프로그래밍을 사용하여 최적의 스필 세트가 수집됩니다.다음으로 라이브 범위에 주석을 붙입니다.compressAnnotation이전에 식별된 최적 유출 세트에 의존하는 알고리즘.이후 오프라인 [55]단계에서 수집된 데이터에 기초하여 온라인 단계에서 레지스터 할당을 실시한다.

2007년, Bouchez 외 연구진은 레지스터 할당을 다른 단계로 분할할 것을 제안했습니다.한 단계는 흘리기 전용이고 다른 단계는 컬러링 [56]및 결합 전용입니다.

다른 기술 간의 비교

하나의 레지스터 할당 기법과 다른 레지스터 할당 기법의 성능을 평가하기 위해 여러 메트릭이 사용되었습니다.레지스터 할당은 일반적으로 코드 품질(즉, 빠르게 실행되는 코드)과 분석 오버헤드(즉, 최적화된 레지스터 할당으로 코드를 생성하기 위해 소스 코드를 분석하는 데 소요된 시간) 간의 트레이드오프를 처리해야 합니다.이러한 관점에서 생성된 코드의 실행 시간과 라이브스 분석에 소요되는 시간은 서로 다른 기술을 [57]비교하기 위한 관련 지표이다.

관련 메트릭이 선택되면 실제 응용 프로그램의 동작을 반영하거나 알고리즘이 대처하고자 하는 특정 문제에 관련됨으로써 메트릭이 적용되는 코드를 사용할 수 있고 문제와 관련되어야 합니다.레지스터 할당에 관한 최신 기사에서는 특히 Dacapo 벤치마크 [58]스위트를 사용합니다.

「 」를 참조해 주세요.

레퍼런스

  1. ^ 디첼 & 맥렐란 1982 페이지 48
  2. ^ Runeson & Nyström 2003, 페이지 242.
  3. ^ a b Eisl et al. 2016, 페이지 14:1
  4. ^ a b Chaitin et al. 1981, 페이지 47
  5. ^ a b c Eisl et al. 2016, 페이지 1
  6. ^ "Latency Comparison Numbers in computer/network". blog.morizyun.com. 6 January 2018. Retrieved 2019-01-08.
  7. ^ Braun & Hack 2009, 페이지 174
  8. ^ Koes & Goldstein 2009, 페이지 21
  9. ^ a b c d Bouchez, Darte & Rastello 2007b, 페이지 103.
  10. ^ 콜롬비아, Brandner & Darte 2011, 페이지 26
  11. ^ "Intel® 64 and IA-32 Architectures Software Developer's Manual, Section 3.4.1" (PDF). Intel. May 2019. Archived from the original (PDF) on 2019-05-25.
  12. ^ "32-bit PowerPC function calling conventions".
  13. ^ Bouchez, Darte & Rastello 2006, 페이지 4.
  14. ^ Horwitz et al. 1966, 43페이지
  15. ^ 패러치 & 리베라토어 1998, 566페이지
  16. ^ Eisl et al. 2017, 페이지 92
  17. ^ Eisl, Leopoldseder & Mössenböck 2018, 페이지 1
  18. ^ a b c Briggs, Cooper & Torczon 1992, 316페이지
  19. ^ 폴레토 & 사르카 1999, 페이지 896
  20. ^ Runeson & Nyström 2003, 페이지 241.
  21. ^ 1975권, 618-619페이지.
  22. ^ 콜롬비아, Brandner & Darte 2011, 페이지 1
  23. ^ 스미스, 램지 & 할로웨이 2004, 페이지 277
  24. ^ a b c Cavazos, Moss & O'Boyle 2006, 페이지 124.
  25. ^ Runeson & Nyström 2003, 페이지 240
  26. ^ 폴레토 & 사르카 1999, 페이지 895
  27. ^ 폴레토 & 사르카 1999, 페이지 902
  28. ^ Wimmer & Mössenböck 2005, 페이지 132.
  29. ^ 요한슨 & 사고나스 2002, 페이지 102
  30. ^ Palenczy, Vick & Click 2001, 페이지 1. 오류:: 도움말
  31. ^ 폴레토 & 사르카 1999, 페이지 899
  32. ^ Eisl et al. 2016, 페이지 2
  33. ^ Traub, Holloway & Smith 1998, 페이지 143.
  34. ^ Traub, Holloway & Smith 1998, 페이지 141.
  35. ^ 폴레토 & 사르카 1999, 페이지 897
  36. ^ Wimmer & Franz 2010, 페이지 170
  37. ^ Mössenböck & Feiffer 2002, 페이지 234.
  38. ^ Mössenböck & Feiffer 2002, 페이지 233.
  39. ^ Mössenböck & Feiffer 2002, 페이지 229.
  40. ^ 로저스 2020
  41. ^ a b c Briggs, Cooper & Torczon 1992, 313페이지
  42. ^ 1982년 차이틴, 페이지 90
  43. ^ a b 안철수 & 백 2009, 페이지 7
  44. ^ Park & Moon 2004, 페이지 736.
  45. ^ 1982년 Chaitin, 99페이지
  46. ^ Park & Moon 2004, 페이지 738.
  47. ^ Briggs, Cooper & Torczon 1994, 페이지 433
  48. ^ 조지 & 아펠 1996, 페이지 212
  49. ^ Park & Moon 2004, 페이지 741.
  50. ^ Eisl et al. 2017, 페이지 11
  51. ^ Cavazos, Moss & O'Boyle 2006, 페이지 124-127.
  52. ^ Eisl et al. 2016, 페이지 4
  53. ^ Diouf et al. 2010, 페이지 66
  54. ^ Cohen & Rohou 2010, 페이지 1
  55. ^ Diouf et al. 2010, 페이지 72
  56. ^ Bouchez, Darte & Rastello 2007a, 페이지 1
  57. ^ 폴레토 & 사르카 1999, 페이지 901-910.
  58. ^ 블랙번2006, 페이지 169
  59. ^ Flajolet, Raoult & Vuillemin 1979.

원천

외부 링크