A small test on onnx library for Rosasm
I made a wrapper of the major onnx functions to make it easier to work with
Attached file without onnx models (need rosasm to open). The necessary dlls are a bit big to upload here, so i posted them also in other file in mega.
Complete version - https://mega.nz/file/Pth0SSwA#7W9qmjeDpsOdUGOivaSnLWLSrxB_ILAS4ZFbUIlaOTE
Executable and Dlls only - https://mega.nz/file/u9QSjR7T#PbMuBhP6QzKVNX9UIZCIvbO1aJ44gJcGuoH87wl90XY
I had to look it up (https://en.wikipedia.org/wiki/Open_Neural_Network_Exchange) - interesting :thumbsup:
Do you have a concrete example or application in mind?
Hi JJ
Not yet (Just the ones i posted - attached here and in the links from mega). I was working on it but had to take a break to try to update RosAsm for the next release. I´m trying to make several major changes, including the ones related to the instring routines (that´s why i talked to you this week about this kind of function, because i´ll definitely need some faster instring functions to the next release)
The problem is that i didn´t updated rosasm since ages, and the last time Betov updated it (when he was active), there was still lots of bugs inside the code and RosAsm internalls are extremelly attached to the interface and still a bit buggy, what makes the updates a hell to do. So i´m trying to speed up and create the necessary dlls to make rosasm work more safelly and unlimited.
About the onnx, i did a small test last year (file attached here and also on the 1st post) that parsed a .onnx file, but the updated functions are the ones inside the file i provided earlier.
If you can´t open it in rosasm, i also uploaded the asm file of it (instead the exe embeded with the source).
Btw, on the attached files, you can do a small test similar to the C ones. You can draw a letter and it returns the overall percentage matching of that image
(https://i.postimg.cc/D85Sm3T8/fdsfsdf-Clipboard01.png) (https://postimg.cc/D85Sm3T8)
Btw, on the source code, i made 2 TITLE's (parts) related to the onnx api itself. One is called "TITLE OnnxHelpers" that contains (for the moment) one single function to handle the onnx enviroment (SimpleCreateEnv) and other Title "TITLE OnnxCalling" that contains wrappers for all onnx functions (at least untill the version i used last year)
I made a serie of all wrappers such as:
CreateStatus GetErrorCode GetErrorMessage CreateEnv
CreateEnvWithCustomLogger EnableTelemetryEvents DisableTelemetryEvents CreateSession
CreateSessionFromArray RunModel CreateSessionOptions SetOptimizedModelFilePath
CloneSessionOptions SetSessionExecutionMode EnableProfiling DisableProfiling
EnableMemPattern DisableMemPattern EnableCpuMemArena DisableCpuMemArena
SetSessionLogId SetSessionLogVerbosityLevel SetSessionLogSeverityLevel SetSessionGraphOptimizationLevel
SetIntraOpNumThreads SetInterOpNumThreads CreateCustomOpDomain CustomOpDomain_Add
AddCustomOpDomain RegisterCustomOpsLibrary SessionGetInputCount SessionGetOutputCount
SessionGetOverridableInitializerCount SessionGetInputTypeInfo SessionGetOutputTypeInfo SessionGetOverridableInitializerTypeInfo
SessionGetInputName SessionGetOutputName SessionGetOverridableInitializerName CreateRunOptions
RunOptionsSetRunLogVerbosityLevel RunOptionsSetRunLogSeverityLevel RunOptionsSetRunTag RunOptionsGetRunLogVerbosityLevel
RunOptionsGetRunLogSeverityLevel RunOptionsGetRunTag RunOptionsSetTerminate RunOptionsUnsetTerminate
CreateTensorAsOrtValue CreateTensorWithDataAsOrtValue IsTensor GetTensorMutableData
FillStringTensor GetStringTensorDataLength GetStringTensorContent CastTypeInfoToTensorInfo
GetOnnxTypeFromTypeInfo CreateTensorTypeAndShapeInfo SetTensorElementType SetDimensions
GetTensorElementType GetDimensionsCount GetDimensions GetSymbolicDimensions
GetTensorShapeElementCount GetTensorTypeAndShape GetTypeInfo GetValueType
CreateMemoryInfo CreateCpuMemoryInfo CompareMemoryInfo MemoryInfoGetName
MemoryInfoGetId MemoryInfoGetMemType MemoryInfoGetType AllocatorAlloc
AllocatorFree AllocatorGetInfo GetAllocatorWithDefaultOptions AddFreeDimensionOverride
GetValue GetValueCount CreateValue CreateOpaqueValue
GetOpaqueValue KernelInfoGetAttribute_float KernelInfoGetAttribute_int64 KernelInfoGetAttribute_string
KernelContext_GetInputCount KernelContext_GetOutputCount KernelContext_GetInput KernelContext_GetOutput
ReleaseEnv ReleaseStatus ReleaseMemoryInfo ReleaseSession
ReleaseValue ReleaseRunOptions ReleaseTypeInfo ReleaseTensorTypeAndShapeInfo
ReleaseSessionOptions ReleaseCustomOpDomain GetDenotationFromTypeInfo CastTypeInfoToMapTypeInfo
CastTypeInfoToSequenceTypeInfo GetMapKeyType GetMapValueType GetSequenceElementType
ReleaseMapTypeInfo ReleaseSequenceTypeInfo SessionEndProfiling SessionGetModelMetadata
ModelMetadataGetProducerName ModelMetadataGetGraphName ModelMetadataGetDomain ModelMetadataGetDescription
ModelMetadataLookupCustomMetadataMap ModelMetadataGetVersion ReleaseModelMetadata CreateEnvWithGlobalThreadPools
DisablePerSessionThreads CreateThreadingOptions ReleaseThreadingOptions ModelMetadataGetCustomMetadataMapKeys
AddFreeDimensionOverrideByName GetAvailableProviders ReleaseAvailableProviders GetStringTensorElementLength
GetStringTensorElement FillStringTensorElement AddSessionConfigEntry CreateAllocator
ReleaseAllocator RunWithBinding CreateIoBinding ReleaseIoBinding
BindInput BindOutput BindOutputToDevice GetBoundOutputNames
GetBoundOutputValues ClearBoundInputs ClearBoundOutputs TensorAt
CreateAndRegisterAllocator SetLanguageProjection SessionGetProfilingStartTimeNs SetGlobalIntraOpNumThreads
SetGlobalInterOpNumThreads SetGlobalSpinControl AddInitializer CreateEnvWithCustomLoggerAndGlobalThreadPools
SessionOptionsAppendExecutionProvider_CUDA SessionOptionsAppendExecutionProvider_ROCM SessionOptionsAppendExecutionProvider_OpenVINO SetGlobalDenormalAsZero
CreateArenaCfg ReleaseArenaCfg ModelMetadataGetGraphDescription SessionOptionsAppendExecutionProvider_TensorRT
SetCurrentGpuDeviceId GetCurrentGpuDeviceId KernelInfoGetAttributeArray_float KernelInfoGetAttributeArray_int64
CreateArenaCfgV2 AddRunConfigEntry CreatePrepackedWeightsContainer ReleasePrepackedWeightsContainer
CreateSessionWithPrepackedWeightsContainer CreateSessionFromArrayWithPrepackedWeightsContainer SessionOptionsAppendExecutionProvider_TensorRT_V2 CreateTensorRTProviderOptions
UpdateTensorRTProviderOptions GetTensorRTProviderOptionsAsString ReleaseTensorRTProviderOptions EnableOrtCustomOps
RegisterAllocator UnregisterAllocator IsSparseTensor CreateSparseTensorAsOrtValue
FillSparseTensorCoo FillSparseTensorCsr FillSparseTensorBlockSparse CreateSparseTensorWithValuesAsOrtValue
UseCooIndices UseCsrIndices UseBlockSparseIndices GetSparseTensorFormat
GetSparseTensorValuesTypeAndShape GetSparseTensorValues GetSparseTensorIndicesTypeShape GetSparseTensorIndices
HasValue KernelContext_GetGPUComputeStream GetTensorMemoryInfo GetExecutionProviderApi
SessionOptionsSetCustomCreateThreadFn SessionOptionsSetCustomThreadCreationOptions SessionOptionsSetCustomJoinThreadFn SetGlobalCustomCreateThreadFn
SetGlobalCustomThreadCreationOptions SetGlobalCustomJoinThreadFn SynchronizeBoundInputs SynchronizeBoundOutputs
SessionOptionsAppendExecutionProvider_CUDA_V2 CreateCUDAProviderOptions UpdateCUDAProviderOptions GetCUDAProviderOptionsAsString
ReleaseCUDAProviderOptions SessionOptionsAppendExecutionProvider_MIGraphX AddExternalInitializers CreateOpAttr
ReleaseOpAttr CreateOp InvokeOp ReleaseOp
SessionOptionsAppendExecutionProvider CopyKernelInfo ReleaseKernelInfo GetTrainingApi
SessionOptionsAppendExecutionProvider_CANN CreateCANNProviderOptions UpdateCANNProviderOptions GetCANNProviderOptionsAsString
ReleaseCANNProviderOptions]
The idea of the wrappers is build a dll to handle this. I need to document it completelly yet, but no time so far.
Hi Guga,
To my old eyes, this looks even more cryptic than the MasmBasic source, so I'll reserve that when I will be reborn as a genius ;-)
You have a bit of work to do, it seems - good luck :thumbsup:
; General purpose macros:
[push | push #1 | #+1] [pop | pop #1 | #+1]
[mov | mov #1 #2 | #+2]
[inc | inc #1 | #+1] [dec | dec #1 | #+1]
[On | cmp #1 #3 | jn#2 M1> | #4>L | M1:]
[call | push #L>2 | call #1]
[move | push #2 | pop #1 | #+2] ; (for mem to mem moves, for exemple)
[If | #=3 | cmp #1 #3 | jn#2 I1>]
[Else_if | #=3 | jmp I9> | I1: | cmp #1 #3 | jn#2 I1>]
[Else | Jmp I9> | I1:]
[End_if | I1: | I9:]
Hi JJ. Those are only the default macro set to make the true code more readable. The syntax in rosasm actually is much similar to Nasm, but with the macros it is easier to make it more human friendly. Here is the TITLE where it begins "TITLE WindowStart":
______________________________________________________________________________________________________________________
Main:
call 'KERNEL32.GetModuleHandleA' &NULL | mov D$hInstance eax
call 'comctl32.InitCommonControlsEx' INITCOMMONCONTROLSEX
call InitInstance MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT
If eax <> 0
call 'USER32.ShowWindow' D$hMain, &SW_SHOW
call 'User32.GetMessageA' MSG, &NULL, 0, 0
While eax <> 0
call 'User32.TranslateMessage' MSG
call 'User32.DispatchMessageA' MSG
call 'User32.GetMessageA' MSG, &NULL, 0, 0
End_While
End_If
call 'GDI32.DeleteObject' D$Dib
call 'GDI32.DeleteDC' D$hdc_dib
call 'GDI32.DeleteObject' D$brush_bars
call 'GDI32.DeleteObject' D$brush_winner
call 'KERNEL32.ExitProcess' 0
______________________________________________________________________________________________________________________
[szAppName: B$ "ONNX Runtime Sample - MNIST", 0]
[wc:
wc.cbSize: D$ len
wc.style: D$ 0
wc.lpfnWndProc: D$ WndProc
wc.cbClsExtra: D$ 0
wc.cbWndExtra: D$ 0
wc.hInstance: D$ 0
wc.hIcon: D$ 0
wc.hCursor: D$ 0
wc.hbrBackground: D$ (&COLOR_WINDOW + 1)
wc.lpszMenuName: D$ 0
wc.lpszClassName: D$ 0
wc.hIconSm: D$ 0]
Proc InitInstance:
Arguments @WindowWidth, @WindowHeight
move D$wc.hInstance D$hInstance
call 'USER32.LoadIconA' 0, &IDI_APPLICATION | mov D$wc.hIcon eax
call 'USER32.LoadCursorA' 0, &IDC_ARROW | mov D$wc.hCursor eax
move D$wc.lpszClassName szAppName
call 'USER32.RegisterClassExA' wc
On eax = 0, ExitP
call CreateDibImageData
call 'USER32.CreateWindowExA' 0, szAppName, szAppName, &WS_OVERLAPPEDWINDOW__&WS_CLIPCHILDREN,
0, 0, D@WindowWidth, D@WindowHeight,
&NULL, 0,
D$hInstance, &NULL
On eax = &FALSE, ExitP
mov D$hMain eax
EndP
See ? No hard to follow the syntax and readable once we previously do the default macro set..
In RosAsm, there´s no hidden statement or macros (as in masm). Everything is done by the user. Except for a few statements to make the code be more easier to follow in the interface, such as TITLE which is used to split the code in different parts that are displayed in Tabs on the gui or the usage of "|" to allow code different opcodes or macros on the same line etc. All the rest is done by the user. I provided a default set of macros (which i always displays on the very 1st TITLE or the initial part of the source) to make it easier to read and follow the true code, but the user can choose either use it or create his own set.
It´s great to keep freedom for the user do whatever syntax or macros he wants without being forced to use hidden statements on the assembler (things such as "offset" in masm, or dword ptr, invoke, If - which is hidden by masm assembler etc etc, which are not asm instructions, but fixed and hidden statements in masm assembler).
It has his pros and contras. Pros because allow the user to create almost everything he wants with whatever coding style he desires, without being forced to use hidden or fixed statements on the assembler. But contra, because in terms of RosAsm development itself, it may turn the internal development a true babel tower, because during the early years of development, different users contributed to the development, and sometimes each one has a different style and therefore different set of macros used internally in rosasm code to actually update rosasm, which lead to some difficulties to improve/update it.
And that´s what i´m currently doing. I´m updating the code in RosAsm itself to follow the default macro set and get rid of the extreme dependency of the interface. The code used to create RosAsm itself contains thousands of global variables that are reused everywhere, instead local variables to make it easier to maintain and follow, not to mention different coding styles in different parts of the development.
Another example of the dificulties i´m facing. In RosAsm, internally it has a function called WordEdge to identify the boundaries used to distinguish what is an equate, a variable etc etc. It is a very old code and extremely slow, that didn´t even used any "Proc / EndP" macro to make it readable or at least faster in terms of function alignment, such as:
_______________________________________________
[OneWordChars: B$ '0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.' 0
OneWordCharsLen: D$ len
Edge: B$ 0 OddWord: 0 BackAnyWhere: &TRUE]
____________________________________________
; WordEdge looks if a char is or not inside OneWordChars and tells if word edge or not.
; we preserve the flags because direction flag may be set on or off by caller:
WordEdge:
pushfd | cld
push ecx, esi, edi
mov ecx D$OneWordCharsLen, edi OneWordChars, B$Edge &TRUE
repne scasb | jne L9>
mov B$Edge &FALSE
L9: pop edi, esi, ecx
popfd
ret
I updated it yesterday to make it work faster and yet more readable, simply using a table, such as:
; WordEdge looks if a char is or not inside OneWordChars and tells if word edge or not.
; No more need to flag preservation, since we didn´t changed any flag on the new code which eventually was set on or off by caller:
[WordEdgeTbl: D$ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
D$ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0,
D$ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
D$ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0,
D$ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
D$ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
D$ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
D$ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
D$ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
D$ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
D$ 1, 1, 1, 1, 1, 1]
;;
A complete review of wordedge fuunction. Now we use a table to find the chars related to a wordedge.
Chars from "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ." are flagged as &FALSE (0)
Otherwise, they are flagged as &TRUE (1).
The table is simply a set of 256 dwords used as a index, where each dword represents the position/value of
a byte (char in our case). So, if eax contains the letter 'A', we will look at the position (65 = 041 in hexa)
since 65 is the value of char 'A' and so on.
;;
; 1st parameter - the input in eax. Didn´t used any "argument" macro to define a parameter yet. Maybe later i´ll update it again
; once fix the other parts of the code that uses this function.
Proc WordEdge:
Uses eax
movzx eax al
mov eax D$WordEdgeTbl+eax*4
mov B$Edge al
EndP
See ? More readable and faster, and fewer instruuctions. Only uses 3 lines of code, and of course the ones in the macro set ("uses" - for push/pop - preserve registers. In case preserving eax. push eax in the start and pop eax before the function exits) and the Proc/EndP macro which is the default push ebp | mov ebp esp, pop ebp ... ret... ) instructions.
Same thing with other internal function to display the equates. It is one called "ShowEquate" written as:
ShowEquate:
mov ebx, eax
mov edi ShowEquateHexa, esi TrashString ; <--- TrashString global variable used everywhere. Why doing like this ?????
While B$esi <> 0
movsb
End_While
mov eax ' =' | stosd | mov eax ' ' ecx 3 | rep stosd
push edi, ebx
std
mov ecx 9
;L1:
Do
If ecx = 5
mov al '_' | stosb
End_If
If ecx = 1
mov al '_' | stosb
End_If
mov al bl | and al 0F | add al '0' | On al > '9', add al 7
stosb | shr ebx 4; | loop L1<
dec ecx
Loop_Until ecx = 0
cld
pop ebx, edi
inc edi
mov D$edi '_h ', D$edi+4 ' [' | add edi 7
mov eax ebx | call WriteEaxDecimal
mov D$edi '] ', B$edi+4 0
call 'USER32.MessageBoxA' D$hwnd, ShowEquateHexa, ShowEquateTitle, &MB_SYSTEMMODAL
ret
Now...i reviewed it completely and it simply is rewritten as:
Proc ShowEquate:
Arguments @hWndMain, @pEquateName, @EquateValue
Structure @TmpString 128, @pStringStartDis 0 ; <--- Using structure macro to reserve enough space in the stack to hold the string to be displayed
Uses ecx, edx, edi
mov edi D@TmpString
C_call FormatStr edi, {B$ "%s = 0x%xh (%d)", 0}, D@pEquateName, D@EquateValue, D@EquateValue ; <-- my clone of ws_printfA. Need to used it in a dll form (FastCRT.FormatStr) - To do later :)
mov B$edi+eax 0
call 'USER32.MessageBoxA' D@hWndMain, D@TmpString, {B$ 'Win Equate Value', 0}, &MB_SYSTEMMODAL
EndP