当前位置: 动力学知识库 > 问答 > 编程问答 >

Inline Assembly Code Does Not Compile in Visual C++ 2010 Express

问题描述:

I have some assembly merge sort code that I obtained from Github and I am trying to embed it into Inline Assembly in C++, but it won't compile and keeps returning these errors:

1>c:\users\mayank\desktop\assembly\assembly\main.cpp(147): error

C2415: improper operand type

The code that I am attempting to run is this:

#include <iostream>

#include <cmath>

#include <stdio.h>

using namespace std;

const int ARRAYSIZE = 30;

int main()

{

int arr[ARRAYSIZE];

int temp_arr[ARRAYSIZE];

int number;

for(int x = 0; x < ARRAYSIZE; x++)

{

number = (rand() % 99) + 1;

arr[x] = number;

}

/*

READ_ARR_LEN:

__asm

{

// Read the length of the array

//GetLInt [30] // Size of input array

//PutLInt [30]

}

GET_ARRAY:

__asm

{

//intel_syntax

// Get values in arr from the user

mov EAX, arr

mov ECX, ARR_LEN

call Read_Arr

// Run Merge Sort on the array

mov EAX, arr

mov EBX, temp_arr

mov ECX, ARR_LEN

call Merge_Sort

// EXIT

};;

*/

Merge_Sort:

__asm

{

// EAX - Array start

// ECX - array length

// Arrays of size 0 or 1 are already sorted

cmp ARRAYSIZE, 2

jl Trivial_Merge_Sort

// Merge_Sort (first half)

// Length of the first half

// ECX /= 2

push ARRAYSIZE

shr ARRAYSIZE, 1

call Merge_Sort

pop ARRAYSIZE

// Merge_Sort (second half)

push arr

push EBX

push ARRAYSIZE

// Length of the second half

// ECX = ECX - ECX/2

mov EDX, ARRAYSIZE

shr EDX, 1

sub ARRAYSIZE, EDX

imul EDX, 4

// Start index of the second half

// EAX = EAX + (ECX/2) * 4

add arr, EDX

push EDX

call Merge_Sort

pop EDX

pop ARRAYSIZE

pop EBX

pop arr

pushad

// Merge (first half, second half)

// Length of first half = ECX/2

// Length of second half = ECX - ECX/2

mov EDX, ECX

shr ECX, 1

sub EDX, ECX

// Start of second half = EAX + (ECX/2) * 4

mov EBX, EAX

mov EDI, ECX

imul EDI, 4

add EBX, EDI

// Index of temp array = 0

sub EDI, EDI

call Merge

popad

// Copy back the merged array from temp_arr to arr

call Merge_Copy_Back_Temp

ret

};

Trivial_Merge_Sort:

__asm

{

// In case of arrays of length 0 or 1

ret

};

Merge:

__asm

{

// Merge two arrays contents.

// The final merged array will be in temp_arr

// Merging is done recursively.

// Arguments:

// EAX - First array's start

// EBX - Second array's start

// ECX - Length of first array

// EDX - Length of second array

// EDI - Index in temp array

pushad

// Handle the cases where one array is empty

cmp ARRAYSIZE, 0

jz First_Array_Over

cmp EDX, 0

jz Second_Array_Over

// Compare first elements of both the arrays

push ARRAYSIZE

push EDI

mov ARRAYSIZE, [arr]

mov EDI, [ARRAYSIZE]

cmp ARRAYSIZE, EDI

pop EDI

pop ARRAYSIZE

// Pick which ever is the least and update that array

jl Update_First_Array

jmp Update_Second_Array

};

Update_First_Array:

__asm

{

// min_elem = min (first elements of first array and second array)

// Put min_elem into the temp array

push dword ptr [EAX]

pop dword ptr [temp_arr + EDI * 4]

add EAX, 4

dec ECX

inc EDI

// Recursively call Merge on the updated array and the

// other array

call Merge

popad

ret

};

Update_Second_Array:

__asm

{

// min_elem = min (first elements of first array and second array)

// Put min_elem into the temp array

push dword ptr [EBX]

pop dword ptr [temp_arr + EDI * 4]

add EBX, 4

dec EDX

inc EDI

// Recursively call Merge on the updated array and the

// other array

call Merge

popad

ret

};

Merge_Copy_Back_Temp:

__asm

{

// Copy back the temp array into original array

// Arguments:

// EAX - original array address

// ECX - original array length

pushad

// For copying back, the destination array is EAX

mov EBX, EAX

// Now, the source array is temp_arr

mov EAX, temp_arr

call Copy_Array

popad

ret

};

Trivial_Merge:

__asm

{

// Note: One array is empty means no need to merge.

popad

ret

};

First_Array_Over:

__asm

{

// Copy the rest of the second array to the temp arr

// because the first array is empty

pushad

mov EAX, EBX

mov ECX, EDX

mov EBX, temp_arr

imul EDI, 4

add EBX, EDI

call Copy_Array

popad

popad

ret

};

Second_Array_Over:

__asm

{

// Copy the rest of the first array to the temp arr

// because the second array is empty

pushad

mov EBX, temp_arr

imul EDI, 4

add EBX, EDI

call Copy_Array

popad

popad

ret

};

Copy_Array:

__asm

{

// Copy array to destination array

// EAX - Array start

// EBX - Destination array

// ECX - Array length

// Trivial case

cmp ECX, 0

jz Copy_Empty_Array

push ECX

sub EDI, EDI

};

copy_loop:

__asm

{

// Copy each element

push dword ptr [EAX + EDI * 4]

pop dword ptr [EBX + EDI * 4]

inc EDI

loop copy_loop

pop ECX

ret

};

Copy_Empty_Array:

__asm

{

ret

};

Read_Arr:

__asm

{

// EAX - array start

// ECX - array length

mov ESI, EAX

sub EDI, EDI

};

loop1:

__asm

{

// Read each element

lea eax,[esi+edx*4]

inc EDI

loop loop1

ret

};

return 0;

}

网友答案:

(Update: In the original code posted in the question there were attempts to address memory as DWORD [address], which is incompatible with the syntax used by Visual C++'s inline assembler as I point out in my answer below.)

Visual C++ uses MASM syntax for its inline assembly, so you need to use DWORD PTR instead of just DWORD. That's what's causing these compilation errors.

See e.g. this table from The Art of Assembly.

网友答案:

This looks like the code is from this github repository.

In that code, GetLInt is actually a NASM macro which is included in an external macro definition file and calls a function proc_GetLInt, which in turn is provided in an object file io.o, the source code is not there. The problem is therefore simply that

  • you didn't realize that GetLint is external code that you're missing

  • even if you took all files from that repository, it would work because NASM macros don't work directly in VC++ inline assembly

  • even if you fixed the macro problem, you still don't have the GetLInt function because it is provided as a linux object file only, you'd have to write it yourself


How do you fix this?

That code was meant to provide a self-contained assembler program that handles all input/output on its own. Since you're inlining it in VC++, you have much more powerful I/O handling at your hands already. Use those instead, i.e. make sure that the values you want to sort are already in arr before the inline assembly starts.

Then, looking at the code: Merge_Sort expects the start of your array in EAX, and its length in ECX. You can get both from your C++ code. When you do that, you no longer need the READ_ARR_LEN and GET_ARRAY blocks from the assembler code.

I am rather reluctant to reproduce parts of the code with modifications as I cannot find a licence file on github that would say I may do so. Let me try to describe: You need to manually move the pointer to arr into EAX and the content of ARRAYSIZE into EBX at the very start of the assembler routine. (*) As I can see, you have already taken care of filling the array with numbers, so there's nothing you need to do there.

Then you need to remove all unnecessary assembler functions and calls to them. You also should either condense all your separate __asm blocks into one or use external variables to conserve and restore registers between blocks (or read the tutorial here, but just using one block works and is less hassle).

Finally, you have to be careful with the stack frames: every call has to have a matching ret. This is easy to stumble over as the merge sort procedure is recursive.

(*) Careful with VC++'s way of treating variables in inside asm blocks, be sure to actually use pointers when you need them.

So all in all, porting this to VC++ is not a trivial task.

分享给朋友:
您可能感兴趣的文章:
随机阅读: