문제

누군가이 몇 줄의 MSIL을 설명 할 수 있습니까? 평가 스택에서 값을 로컬 변수로 이동하는 이유는 무엇입니까?

다음 MSIL 코드는 단일 인수 (문자열)를로드하고 메소드를 호출하여 BOOL을 반환 한 다음 해당 BOOL 값을 반환합니다. 내가 이해하지 못하는 것은 STLOC.0을 호출하기 위해 로컬 변수에 메소드의 리턴 값을 저장 한 다음 다음에 레이블이 붙은 라인 (불필요한 것으로 보임)으로 명시적인 무조건 제어 전송을 수행하는 이유입니다. 반환하기 전에 평가 스택.

.maxstack 1
.locals init ([0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: call bool FuncNameNotImporant::MethodNameNotImporant(string)
L_0007: stloc.0 
L_0008: br.s L_000a
L_000a: ldloc.0 
L_000b: ret 

이것이 왜 그렇게하는지에 대한 나의 최선의 추측은 평가 스택의 값이 실제로 반환하기 전에 부울 값인지 확인하기 위해 일종의 유형 검사를 수행하는 것입니다. 그러나 나는 다음 줄로 향한 명백한 점프에 대해 단서가 없다. 어쨌든 거기에 가지 않겠습니까? 이 메소드의 C# 소스 코드는 한 줄에 불과하므로 메소드 결과를 반환합니다.

도움이 되었습니까?

해결책

디버거 에서이 기능을 열면 디버그 모드에서 코드를 컴파일하여 다음과 같습니다.

bool foo(string arg)
{
    return bar(arg);
}

설정할 수있는 3 가지 중단점이 있습니다.

  1. 함수의 오프닝 버팀대에서.
  2. "반환"라인에서.
  3. 함수의 닫는 곳에서.

오프닝 브레이스에서 중단 점을 설정한다는 것은 "이 기능이 호출 될 때 파손"을 의미합니다. 그렇기 때문에 방법의 시작 부분에 NO-OP 명령이있는 이유입니다. 브레이크 포인트가 오프닝 브레이스에 설정되면 디버거는 실제로 NO-OP에 설정합니다.

닫는 브레이스에서 중단 점을 설정한다는 것은 "이 기능이 종료 될 때 파손"을 의미합니다. 이를 위해서는 기능에 중단 지점을 설정할 수있는 IL의 단일 리턴 명령어가 있어야합니다. 컴파일러를 사용하면 임시 변수를 사용하여 리턴 값을 저장하고 변환 할 수 있습니다.

return retVal;

~ 안으로

$retTmp = retVal;
goto exit;

그런 다음 메소드 하단에 다음 코드를 주입합니다.

exit:
return $ret;

또한 디버그 모드에서 컴파일러는 생성 된 코드에 대해 바보입니다. 기본적으로 다음과 같은 작업을 수행합니다.

GenerateProlog();
foreach (var statement in statements)
{
    Generate(statement);
}
GenerateEpilog();

귀하의 경우에는 다음과 같이합니다.

return foo(arg);

변환 : :

; //this is a no-op
bool retTemp = false;
retTemp = foo(arg);
goto exit;
exit:
return retTemp;

컴파일러가 "슬라이딩 창 최적화"를 수행하는 경우 해당 코드를보고 중복성이 있음을 알 수 있지만 컴파일러는 일반적으로 디버그 모드에서는이를 수행하지 않습니다. 컴파일러 최적화는 변수 제거 및 재주문 지침과 같은 작업을 수행하여 디버깅이 어렵습니다. 디버그 빌드의 목적은 디버깅을 활성화하는 것이므로 최적화를 켜는 것이 좋지 않습니다.

릴리스 빌드에서 코드는 그렇게 보이지 않습니다. 컴파일러가 개구부 및 닫는 버팀대에서 중단 점을 활성화하기위한 특수 코드를 소개하지 않기 때문입니다.

return bar(arg);

그것은 매우 간단하게 보입니다.

그러나 주목할만한 점은 C# 컴파일러가 소매 빌드에서도 슬라이딩 윈도우 최적화를 많이한다고 생각하지 않는다는 것입니다. 이러한 최적화의 대부분은 기본 프로세서 아키텍처에 의존하기 때문에 JIT 컴파일러에 의해 수행되기 때문입니다. C# 컴파일러에서 프로세서가 불가능한 프로세서조차도 최적화를 수행하면 코드를 최적화 할 수있는 JIT의 능력을 방해 할 수 있습니다 (최적화되지 않은 코드 생성에 의해 생성되는 패턴을 찾고 있으며, 최적화 된 IL을 볼 수 있다면 얻을 수 있습니다. 혼란스러운). 따라서 일반적으로 관리 코드 컴파일러는 수행하지 않습니다. Dead Code Detection 및 Live 변수 분석과 같이 "비싼 일"(JIT가 런타임에서 수행하고 싶지 않음)을 수행하지만 슬라이딩 윈도우 최적화로 해결 된 문제는 해결되지 않습니다.

다른 팁

디버그 또는 릴리스 모드에서 컴파일하고 있습니까? 릴리스 모드에서는 다음과 같습니다.

.method private hidebysig static bool Test1(string arg) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: call bool FuncNameNotImportant::MethodNameNotImportant(string)
    L_0006: ret 
}

당신이보고있는 분기는 아마도 디버거 지원을위한 것입니다.

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