Is it practical, or even possible, to generate ASM code from the C source ?
It is possible.
If not, where is the hex file stored ?
It's stored in a mysteriously named temp directory that is supplied by some combination of java and the OS. The best way to find it is to run on verbose compilation and look at the log file. It will be something like:
/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876
In there will be a .elf file, which is the object code + symbol information + debugging information, and the best way to look at the assembler is to disassembly that binary with a command like "avr-objdump -SC *.elf" (avr-objdump is inside the arduino in the same directory with avr-gcc and a bunch of other tools.) This way you get link-time optimized code that has been properly relocated to final addresses.
Since the compilation is using link-time-optimization, it is no longer useful to use objdump on individual .o files (unless you recompile them without the -lto options.) Even then, all the jump targets are "unresolved" and show as 0, which makes it difficult to interpret.
Once you've done the "verbose" output from the IDE, you can also copy and modify individual file compiles to produce "intermediate" assembler files. But they're really ugly - they include all the assembler directives to produce the debugging info as well as the actual code.
Here are some examples, using Blink...
Partial cut&paste from verbose compiler output:
Generating function prototypes...
"/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/tools/avr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10805 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/cores/arduino" "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/variants/standard" "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/sketch/Blink.ino.cpp" -o "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/preproc/ctags_target_for_gcc_minus_e.cpp"
"/Applications/arduino/Arduino-1.8.5.app/Contents/Java/tools-builder/ctags/5.8-arduino11/ctags" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/preproc/ctags_target_for_gcc_minus_e.cpp"
Compiling sketch...
"/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/tools/avr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10805 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/cores/arduino" "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/variants/standard" "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/sketch/Blink.ino.cpp" -o "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/sketch/Blink.ino.cpp.o"
Compiling libraries...
Compiling core...
"/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/tools/avr/bin/avr-gcc" -c -g -x assembler-with-cpp -flto -MMD -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10805 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/cores/arduino" "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/variants/standard" "/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/cores/arduino/wiring_pulse.S" -o "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/core/wiring_pulse.S.o"
Here's the extracted compile command for the user sketch file itself, with some of the changeable options highlighted.
"/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/tools/avr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10805 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/cores/arduino" "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/variants/standard" "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/sketch/Blink.ino.cpp" -o "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/sketch/Blink.ino.cpp.o"
It's nicely "fully specified", so you can paste it into some command-line window (at least on Mac/Linux) without having to set up anything else.
You can change the "-c" (means "compile only") to "-S" (generate assembly), get rid of "-flto" and give it a different output file:
"/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/tools/avr/bin/avr-g++" -S -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10805 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/cores/arduino" "-I/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/variants/standard" "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_build_821876/sketch/Blink.ino.cpp" -o foo.s
And you get about 1000 lines of foo.s that includes the "intermediate" assembler:
.file "Blink.ino.cpp"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
.text
.Ltext0:
.cfi_sections .debug_frame
.section .text.setup,"ax",@progbits
.global setup
.type setup, @function
setup:
.LFB112:
.file 1 "/var/folders/jz/5yb8f2hr8xjcpf0059bsfz4r0000gn/T/arduino_modified_sketch_211523/Blink.ino"
.loc 1 27 0
.cfi_startproc
push r28
.LCFI0:
.cfi_def_cfa_offset 3
.cfi_offset 28, -2
push r29
.LCFI1:
.cfi_def_cfa_offset 4
.cfi_offset 29, -3
/* prologue: function */
/* frame size = 0 */
/* stack size = 2 */
.L__stack_usage = 2
.LVL0:
.LBB5:
.LBB6:
.LBB7:
.file 2 "/Applications/arduino/Arduino-1.8.5.app/Contents/Java/hardware/arduino/avr/cores/arduino/HardwareSerial.h"
.loc 2 121 0
ldi r18,lo8(6)
ldi r20,0
ldi r21,lo8(-62)
ldi r22,lo8(1)
ldi r23,0
ldi r24,lo8(Serial)
ldi r25,hi8(Serial)
call _ZN14HardwareSerial5beginEmh
.LVL1:
.LBE7:
.LBE6:
.loc 1 31 0
ldi r22,lo8(1)
ldi r24,lo8(13)
call pinMode
I dread the thought of having to use a Windows machine to debug. I use Mint.
Why would this involve Windows at all?
The "obvious" heavy-duty "debug and look at object code" toolset is "Atmel Studio", which runs only on Windows.
If you generate verbose output during compilation, a listing file is stored in some temp directory,
No, it's not. Note that by the time lto is through with your sketch, it's probably inlined a lot of called-one-time functions like "loop" and "setup", making the output a bit difficult to interpret.
As others have implied while not answering your question, you are unlikely to achieve great speedups by using assembler rather than C. However, I do find that it can be useful to look at the assembly code to see what the compiler is doing with "strange" IO manipulations and such.