'assembler'에 해당되는 글 3건

  1. 2010.01.03 [MIPS] Assembler 코드 살펴보기 - 4
  2. 2009.12.26 [MIPS] Assembler 코드 살펴보기
  3. 2009.06.18 맥에서 프로파일링 하기
Embedded2010.01.03 07:59
이번에는  Loop문이 어떻게 바뀌는지에 대해서 살펴 봅니다.
예제를 새로 짜는 것 보다는 기존에 있던것을 가지고 한번 해보겠습니다.

아래는 NEWLIB 라이브러리에 있는 memset 함수입니다.
참고로, newlib은 c에서 사용하는 여러가지 라이브러리를 embedded  등에서 사용할 수 있도록 제공해주는 라이브러리입니다. 다양한 프로세서에 포팅되어 있습니다. 대부분의 함수가 C로 코딩되어 있지만, 속도가 필요한 부분이나 프로세서에 종속적인 부분에 대해서는 assembler로 되어 있습니다.

주어진 어드레스 m에서 시작해서 주어진 값 c를   n개의 메모리에 차례로 기록하는 것입니다.

........
#define PREFER_SIZE_OVER_SPEED 1
_PTR
_DEFUN (memset, (m, c, n),
        _PTR m _AND
        int c _AND
        size_t n)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) || defined(__mips16)
  char *s = (char *) m;

  while (n-- != 0)
    {
      *s++ = (char) c;
    }

  return m;
#else
  char *s = (char *) m;
  int i;
  unsigned wordtype buffer;
......

#ifdef -else에서 #else는 복잡한(?) 판단문을 가지고 있으므로 위의 #ifdef를 이용하기로 합니다.
그래서 PERFER_SIZE_OF_SPEED를 Define하여서 #ifdef로 컴파일 되게 합니다.

코드를 보면 S라고 하는 스타팅 어드레스는 주어진 인자 m으로 세팅됩니다.
이후 while에서 n을 하나씩 밑으로 카운트 하면서 주어진 인자 c를  S포인터에 기록합니다.
이후 S 포인터를 하나씩 증가시킵니다.


컴파일 커맨드는 아래와 같습니다.
mips-elf-gcc -S -O2 memset.c -o memset.s

컴파일 결과는 아래와 같습니다.

        .file   1 "memset.c"
        .section .mdebug.abi32
        .previous
        .gnu_attribute 4, 1
        .text
        .align  2
        .globl  memset
        .set    nomips16
        .ent    memset
        .type   memset, @function
memset:
        .frame  $sp,0,$31               # vars= 0, regs= 0/0, args= 0, gp= 0
        .mask   0x00000000,0
        .fmask  0x00000000,0
        .set    noreorder
        .set    nomacro

        beq     $6,$0,$L7
        move    $2,$4

        sll     $5,$5,24
        sra     $5,$5,24
        move    $3,$4
$L3:
        addiu   $6,$6,-1
        sb      $5,0($3)
        bne     $6,$0,$L3
        addiu   $3,$3,1

$L7:
        j       $31
        nop

        .set    macro
        .set    reorder
        .end    memset
        .size   memset, .-memset
        .ident  "GCC: (GNU) 4.4.2"

위에서 보면 우선 $6이 $0과 같으면 $L7으로 분기하도록 되어 있습니다.
$0는 0 레지스터 값이므로 항상 0을 가집니다. 즉 주어진 카운트 값이 0이면 더이상 할 필요가 없으므로, 그냥 일을 마치고 돌아가는 것입니다.

따라서 $6이 인자 c (count)에 해당된다는 것을 알수 있습니다.

그리고 그 밑에

        sll     $5,$5,24
        sra     $5,$5,24

구분은 쉬프트 구문인데 $5를 왼쪽으로 24번 보냈다가 오른쪽으로 다시 24번 보내는 구문입니다.
뻉뻉이 돌리는 것인데요, 이렇게 하면 상위 24비트는 모두 0가 됩니다. 위의 구문은 그냥

and $5 , $5 , 0xFF와 같은 구문이 됩니다.

이렇게 해서 하위 8비트 즉 하위 1Byte만 사용한다는 것입니다. 인자 리스트를 쭈욱 보면 c에 해당하는 것이
함수 중간에서 char로 사용하고 있으므로 $5는 c에 해당하는 것임을 눈치밥으로 알수 있습니다.

      .......
      *s++ = (char) c; <-- 요기서  c가 char로 쓰이므로 불필요한 상위 24비트를 날려버리는 것이빈다.
      ....

이렇게 해서 필요한 / 사용할 데이터 타입과 변수들을 정리해 두고 whil에 해당하는 루프를 시작합니다.


$L3:
        addiu   $6,$6,-1
        sb      $5,0($3)
        bne     $6,$0,$L3
        addiu   $3,$3,1

을 살펴 봅니다.
$6이 카운트 값이라고 하였으니 addiu   $6,$6,-1  구문은 자연스럽게
 ...
 while (n-- != 0)
  ...
에서 n--에 해당함을 알 수 있습니다.
이제 갑자기 나온 레지스터 $3 이 보이는데 코드를 보면 레지스터 $5의 값을 어드레스 $3에 기록하는 것을 알 수 있습니다. 즉 메모리에 쓰는 어드레스를 $3에 기록합니다. 따라서 C코드에서 변수 s에 해당하는 것이 $3임을 알수 있습니다.

이후 카운트 값 레지스터 $6이 0이 아니면 계속 루프를 돌고 0이면 밑으로 내려가도록 되어 있습니다.
addiu $3,$3,1 은
어드레스를 1 증가시키는 것에 해당합니다.

      *s++ = (char) c;

에서 s++에 해당합니다.

이는 loop를 돌때 마다 해야 하므로 branch delay slot에 해당하는  BNE다음 구문으로 집어 넣습니다.

이렇게 해서 loop를 돌고 끝나면 jump로 복귀하게 됩니다.
복귀 문인 j 문 다음에 있는 NOP는 그냥 Delay Slot을 사용하지 않도록 하기 위해서 0으로 넣어버린 것입니다.






Posted by GUNDAM_IM
Embedded2009.12.26 09:04
굳은 머리 돌리기 위한 간단한 어셈블러 해석입니다.
오래간만에 어셈블러 한번 볼려고 하니 힘드네요
MIPS에서 어셈블러와 C의 연결을 볼려고 간단한 테스트를 해보는 것을 정리하였습니다.

1. 간단한 함수


int foo( int i , int j )
{
  return i * j;
}

위 코드를 컴파일 해서 어셈블러를 확인합니다.

mips-elf-gcc -S -march=mips32 -O2 test.c -o test.S

그러면

                            LIBCFLAGS="-g -O2 ${flags}" \
        .file   1 "test.c"
        .section .mdebug.abi32
        .previous
        .gnu_attribute 4, 1
        .text
        .align  2
        .globl  foo
        .set    nomips16
        .ent    foo
        .type   foo, @function
foo:
        .frame  $sp,0,$31               # vars= 0, regs= 0/0, args= 0, gp= 0
        .mask   0x00000000,0
        .fmask  0x00000000,0
        .set    noreorder
        .set    nomacro

        j       $31
        mul     $2,$5,$4

        .set    macro
        .set    reorder
        .end    foo
        .size   foo, .-foo
        .ident  "GCC: (GNU) 4.4.2"

과 같은 코드를 얻을 수 있습니다.

코드를 보면 jump 다음에 mul을 하는 것을 알 수 있습니다. 즉 delay slot을 기본적으로 architecture에서 가지고 가므로, jump 명령이 먼저와도 분기를 마칠때에는 레지스터에 값이 기록되게 되어 있습니다.

리턴되는 값은 2번 레지스터에 기록되고
인자는 4번과 5번으로 기록됩니다.

2. unsigned 64비트 곱셈 test


unsigned long long  foo( unsigned long long i , unsigned long long  j)
{
  return i* j ;
}

../local/bin/mips-elf-gcc -S test3.c -o test3.S -O2 -march=mips32
으로 컴파일해서 결과를 보면 아래와 같습니다.

        .file   1 "test3.c"
        .section .mdebug.abi32
        .previous
        .gnu_attribute 4, 1
        .text
        .align  2
        .globl  foo
        .set    nomips16
        .ent    foo
        .type   foo, @function
foo:
        .frame  $sp,0,$31               # vars= 0, regs= 0/0, args= 0, gp= 0
        .mask   0x00000000,0
        .fmask  0x00000000,0
        .set    noreorder
        .set    nomacro

        mul     $2,$4,$7
        multu   $7,$5
        mflo    $9
        mfhi    $8
        mul     $3,$6,$5
        addu    $2,$3,$2
        addu    $2,$2,$8
        j       $31
        move    $3,$9

        .set    macro
        .set    reorder
        .end    foo
        .size   foo, .-foo
        .ident  "GCC: (GNU) 4.4.2"

입력이 64비트 두개의 값이므로,
4개의 레지스터가 입력으로 들어와야 합니다.


$4,
$7
$5
$6이 입력이 됩니다.

64비트 입력은 두개의 레지스터이고 두 64비트의 곱셈은

              A B
x            C D
----------------
         [B x D]
    [A x D]
    [B x C]
[A x C]
-------------
Result

입니다.

그런데 결과값이 64비트만 필요하므로 [A x C]는 불필요 합니다. 따라서 곱셈이 3번만 하면됩니다.
위의 코드에서 곱셈이 3번 나오는 이유입니다.

$7과 $5를 곱하면 그 결과가 Hi,Lo레지스터에 기록됩니다.
이를 $8,$9에 옮겨 두게 됩니다. [B x D]가 $8 $9입니다.

$4 $7을 곱한것은 그 결과로 하위 32비트가 필요하므로 [A x D] 가 됩니다. 따라서 공통으로 쓰이는 $7이 D임을 알수 있고, [ A : B ]는 [$4,$5]임을 알수 있습니다.
나머지 입력인 $6이 [ $6 : $7 ] 입니다.

$6 x $5를 한 하위 32비트를 사용하게 되므로 이 값이 $3에 들어갑니다.

최종적으로는 결과값의 상위 32비트에 해당되는 값은 각각의 덧셈의 결과이므로
$2 + $3 + $8이 그 결과값인 상위 32비트입니다.

이 결과값이 $2에 그리고 [BxD]의 결과값이 $3으로 옮기게 되므로 최종 결과치는
[$2:$3]이 됩니다.




'Embedded' 카테고리의 다른 글

[MIPS] Assembler 코드 살펴보기 - 3  (0) 2009.12.30
[MIPS] Assembler 코드 살펴보기 - 2  (0) 2009.12.29
[MIPS] Assembler 코드 살펴보기  (0) 2009.12.26
Fedora 메일서버 세팅  (2) 2009.12.21
[MIPS] Simulator  (0) 2009.12.20
MPEG 1/2 Reference Site  (0) 2009.12.13
Posted by GUNDAM_IM
MAC Life2009.06.18 23:30
맥에서 프로파일링을 하는 것에 대해서 설명합니다.
맥은 프로파일링 툴로서 SHARK라는 툴을 제공합니다.

Shark는 XCODE에서도 사용할 수 있지만 Command Line에서도 사용할 수 있습니다.

그리고 아무렇게나 띄워 놓고, O/S 상에서 돌아가는 태스크 중에서 선택해서 프로파일링 을 할 수 있습니다.
이번에는 3번째 방법인 아무렇게나 띄워 놓고 필요한 프로그램을 프로파일링 하는 것에 대해서 설명합합니다.
나머지 2개는 나중에 필요해지면 그때 가서 진행하도록 하겠습니다. - 그때 공부해서 하겠습니다. ㅋㅋ

일단 Shark는 /Application 폴더 있지 않고 /Developer 폴더에 있습니다.

/Developer/Applications/Performance Tools/shark

입니다.

사용자 삽입 이미지

실행하면 위와 같은 간단한 창이 뜹니다.

제일 왼쪽에 시작 버튼이 있고요, 제일 오른쪽에 실행에 대한 방식을 선택하는 Combo Box 입니다.

사용자 삽입 이미지
그 콤보 박스에서 제일 위에 있는 Launch를 선택합니다. 이후에는..
자동으로 실행하면 됩니다.

시작 버튼을 누르면 다음과 같은 창이 나옵니다.

사용자 삽입 이미지
칸을 하나하나 설명하면,
제일 위의 Executable은 실행 프로그램을
Working Dir은 실행 폴더를
Arguments는 옵션을 줍니다.

일단 다음과 같이 설정합니다.

사용자 삽입 이미지
Application : /Users/kevinim/Documents/etri_svc/JSVM/jsvm/bin/AvcRewriterStaticd
Working Dir : /Users/kevinim/Documents/etri_svc/JSVM/
Arguments : output/Coral.svc.264 output/Coral.svc.avc.2.264

으로 설정합니다.

이것은 환경에 따라 틀리므로 적당하게 설정하시고
시작 버튼을 누르면 한참 진행한 후에 결과가 나옵니다.

사용자 삽입 이미지
위의 그림은 작업이 끝난 후에 결과를 분석하는 과정입니다.
그냥 자동으로 수행됩니다.

이후에 각 함수별로 잘 정리된 그림이 나옵니다.

사용자 삽입 이미지
그리고 위의 창에서 Chart TAB을 선택하면 다음과 같은 그림이 나옵니다.

사용자 삽입 이미지
왼쪽의 그래프는 Call Stack Depth를 표시해 줍니다. 얼마나 함수가 호출되어 왔는지에 대한 정보를 보여줍니다.
이중에서 제일 깊이 된것 중에 하나를 선택하면 창의 오른쪽에 그에 해당하는 Call Stack Frame을 보여줍니다.

사용자 삽입 이미지
위의 그림은 Call Stack에 대한 그립니다.
더블 클릭하면 해당 함수로 들어갑니다.

사용자 삽입 이미지
창의 왼쪽 밑에 보면 확대 축소를 컨트롤하는 슬라이드 버튼이 있습니다. 이것으로 확대를 하면 아래와 같은 그림이 나옵니다.


사용자 삽입 이미지
세로로 노란색 부분이 선택한 호출 부분이고 가로로 노란색이 부하가 가장 많이 걸리는 함수 등에 대한 부분입니다.


다시 프로파일 창에 들어가서

로드가 많이 걸리는 함수를 찾아서 더블클릭하면 다음과 같이 창이 됩니다.

사용자 삽입 이미지
소스만 보는 창이고, 어셈블를 본다든지 두개 다 보는 것도 가능합니다.
물론 이렇게 까지 볼일이 없겠지만 하여튼 유용하기도 합니다.

사용자 삽입 이미지
위의 창은 어셈블러와 C를 같이 보여주는 창입니다.

인라인 함수로 메모리를 모두 0으로 초기화 시키는것에 생각보다 시간이 많이 걸리는 것을 알수 있습니다.

그리고 컬럼에서 [!] 로 표시된 컬럼이 있습니다. 이것은 최적화에 대한 정보를 알려주는 컬럼입니다.

사용자 삽입 이미지
위에서 보는 것 처럼 함수가 작고 자주 호출하는 것은 인라인으로 해보는게 어떻겠냐고 알려줍니다.

실제 코드는 2가지 타입이 있는데, 디버깅 정보가 포함된 버전과 아닌 일반 버전이 있습니다.
양측간에 차이점이 있고 이로 인해서 프로파일링 결과가 조금씩 차이가 나는 것을 감안하고 보면
도움이 됩니다.

코드 최적화는 다른 문제여서 그것은 언젠가 기회가 되면 다시 설명하도록 하겠습니다.
여기서는 코드 최적화 할일이 없어서요..




'MAC Life' 카테고리의 다른 글

SIGGRAPH [2]  (0) 2009.08.07
CUDA on MAC OS  (0) 2009.06.23
맥에서 프로파일링 하기  (0) 2009.06.18
OSX용 YUV Player 입니다.  (0) 2009.06.16
나의 다섯번째 애플~~  (1) 2009.03.30
맥에서 개발한다는 것은..  (0) 2009.03.19
Posted by GUNDAM_IM