Re: C No Operation doubt

Discussion in 'C Programming' started by Keith Thompson, May 26, 2014.

  1. Something I'm not sure anyway has asked directly:

    Why do you want to do this?
    Keith Thompson, May 26, 2014
    1. Advertisements

  2. You must have a specific machine code routine which contains the instruction
    "NOP" (in whatever instruction set) there's no way of doing that in C.
    There's no C language construct which expresses the idea "execute an
    instruction but do nothing".

    If you want code that delays but has no other effect, use a volatile to
    prevent the loop being optimised away to nothing. If you want any
    specific instruction sequence, use the asm keyword and the inline
    Malcolm McLean, May 26, 2014
    1. Advertisements

  3. That clarifies what you want to do, but not why you want to do it.

    Why do you want to generate a NOP instruction? If you can explain your
    requirement, we might be able to suggest a different way to accomplish
    Keith Thompson, May 26, 2014
  4. Keith Thompson

    Joe Pfeiffer Guest

    You got several correct answers. Just in case they didn't make it
    through your news server: C can't do that. The only way to do it will
    involve whatever your compiler uses for inline assembly code.
    OK, you really do want a NOP. And, as both Keith and I have asked
    before: what are you really trying to do that a NOP in your code seems
    like the solution?
    Joe Pfeiffer, May 26, 2014
  5. Keith Thompson

    Kaz Kylheku Guest

    You are still not clear, because you have not defined what you mean
    by "C code".

    Do you mean

    1. strictly conforming ISO C code (1990, 1999 or 2011)? Or:

    2. some characters aceptable to my C compiler on my platform?

    If by "C code" you mean #2, then here is "C code" that compiles to NOP
    under Linux (on x86 Intel).

    char main[] =
    0x90, /* nop */
    0xC3 /* ret */

    In this manner, we can write a function that consists precisely of the
    instructions we weant: NOP and RET.

    The "standard C" language doesn't have any support for selecting specific machine
    instructions. Some compilers provide "inline assembly" in ther C language
    dialects for this, and as you can see from the above, there are even other
    ways to hack it.
    Kaz Kylheku, May 26, 2014
  6. Keith Thompson

    jacob navia Guest

    Le 27/05/2014 06:49, ralph a écrit :
    This nops are used for alignment purposes. And they aren't specific of
    windows applications. Gcc uses them too.
    jacob navia, May 27, 2014
  7. Keith Thompson

    David Brown Guest

    I believe gcc usually uses alignment assembler directives for alignment,
    rather than nops. That is certainly true at the start of functions -
    you have an alignment directive, then the start of the function. MS
    compiler apparently puts these nops after the function label, so that
    they are actually executed by the cpu rather than being skipped. Since
    MS's compiler writers are not stupid, and know perfectly well how to
    make aligned functions without wasting cpu cycles, it must be assumed
    that they have a different purpose - such as leaving space for patching
    at run-time, as supported by MS VC.

    nops may well be generated to get better alignments for loops in the
    middle of a function, and gcc certainly generates them on processors
    with delay slots on branches (such as MIPS) if it cannot fill the delay
    slot with something useful.
    David Brown, May 27, 2014
  8. Keith Thompson

    jacob navia Guest

    Le 27/05/2014 12:45, David Brown a écrit :
    Look, a compiler generates assembmer instructions. The compilers that
    generate directly binary instructions are rare, but they do exst, I
    worked in a Pascal compiler for a french company that did that.

    The assembler then, takes those assembler instructions (ascii text) and
    generates the binary opcodes and the other stuff associated with an
    object file.

    When gcc generates
    .align 4
    the assembler fills the necessary bytes with SOMETHING, in most cases is
    a NOP.

    Of course there are two situations:

    1) Within a function, to align the starting head of a loop, for
    instance, the compiler may generate NOPs that will be maybe executed The
    time "wasted" in NOPs is gained if the loop is done more than a certain
    number of times because the alignment improves the speed of execution.

    2) Outside a function. If the machine arrives at an alignment OUTSIDE a
    function that means that the machine is executing instructions that
    should never be executed and that something is horribly wrong. In those
    cases some compilers align with int 3, a one byte instruction that calls
    the debugger or the OS.

    3) There are many types of alignment instructions that do not do
    anything useful but they consume time and code space. I have a table
    somewhere with those up to 7 bytes long. In lcc-win I do not use any
    alignment but an alignment at an 8 byte boundary for functions. I think
    that aligning too much wastes code space, and that is a heavy price to
    pay in speed. I try to generate code as small as possible.

    jacob navia, May 27, 2014
  9. Keith Thompson

    David Brown Guest

    I didn't say anything about generating binary instructions - I said it
    uses assembler directives. Assembler directives are instructions to the
    assembler, rather than assembly instructions. An alignment directive
    will tell the compiler to skip as many bytes as necessary to reach a
    point with the desired alignment, and is the preferred way to handle
    this sort of thing - it saves the compiler from having to know the
    actual length of all the assembly instructions.

    The assembler will do one of two things. It will either fill the space
    with a constant value (which might be 0 by default, but might be 0xff if
    the target is going to be loaded in flash, and might be chosen by the
    user from options), or it will generate separate sections and let the
    linker or loader figure out what to put in the empty spaces. The linker
    or loader might fill the spaces with a constant value, or it might
    simply leave the unused memory unwritten.

    Many cpus encode a "nop" as 0x00, meaning that the space might
    coincidentally by a "nop", but it is far from universal.

    What is important here is that the cpu is never asked to execute the
    instructions, or filler, or uninitialised values, that are in the space
    created by the alignment directive.
    Yes, within a function the space for alignment needs to be a NOP (or
    multiple NOPs, or a multi-byte NOP, as necessary).
    I suppose that is possible, though I haven't seen it. Most of my code
    is bare-metal - there is no OS to call. But I'll agree that it is
    something that some compilers might do.

    My point here is that the choice is made by the assembler or the linker
    - not the compiler (i.e., gcc) as you suggested.
    David Brown, May 27, 2014
  10. Keith Thompson

    Ken Brody Guest

    It's not that "nobody can answer this question", it's that the answer is
    "you can't do it"(*).

    (*) Well, you can, *if* you use an implementation-specific method to force
    the compiler to do so. Usually, this would be some sort of assembly
    language directive, like "_asm()".
    As one would expect.


    You still haven't answered the "why" question.
    Ken Brody, May 27, 2014
  11. Keith Thompson

    BartC Guest

    Between an unconditional jump, and a label, the padding doesn't need to be
    executable (although it's helpful if it is, for disassembly purposes). For
    example, between the end of stmt1 here, and the else:

    if (cond)
    BartC, May 28, 2014
  12. Keith Thompson

    BartC Guest

    But is a perfectly reasonable thing to do, if done efficiently and
    transparently enough that it doesn't noticeably impact on the compilation

    (I used to generate binary output, now I generate asm source when the final
    result is to be a traditionally built executable. I can't be bothered with
    the details of object-code and executable formats any more.)
    BartC, May 28, 2014
  13. Keith Thompson

    David Brown Guest

    That's true, but there is seldom need for any sort of padding in such
    circumstances. It makes sense for functions to be cache-line aligned,
    and it /might/ make a difference for some loops to be aligned, but I'd
    be surprised if the alignment helped cases like this more than the
    wasted bandwidth (of extra code size) hurt.
    David Brown, May 28, 2014
  14. Keith Thompson

    David Brown Guest

    You are correct that a separate assembler pass will always have a build
    time impact, but usually the impact is negligible. gcc uses a separate
    assembler, and the assembly time is very small compared to the
    compilation time (though to be fair, gcc is not known as the speediest
    compiler). The time to write the file to the disk is usually avoided by
    using pipes rather than files - and even if files are used, these are
    usually kept in memory.
    David Brown, May 28, 2014
  15. Keith Thompson

    James Kuyper Guest

    That was perfectly clear. The question is WHY do you "want to write the
    C code, which compiles to a NOP". You aren't being asked this question
    just out of nosiness, there's a very good reason why that's an important
    question. (see below)
    Many people have correctly answered the question: C doesn't provide any
    mechanism that's guaranteed to that this affect. Good compilers
    generally avoid generating unnecessary NOPs, so there's not even any
    approach that's likely to have that effect. Therefore, you can't do what
    you want to do. That's why you need to fall back to the question "why do
    I want to do it?" You're going to have to find some method other than
    "writ[ing] C code which compiles to a NOP" to achieve that goal. The
    only way to determine which alternative is useful, is to know what your
    goal actually is. You have every right not to tell us. However, the cost
    of making that decision is that no one's going to be able to help you
    figure out how to achieve it.
    James Kuyper, May 28, 2014
  16. (snip, someone wrote)
    The OS/360 (and successor) assemblers have a CNOP (conditional
    NOP) instruction that will generate the appropriate bytes to
    pad to a specified boundary. You can ask for any offset within
    a 4 or 8 byte multiple.

    There is a 2 byte (NOPR) and 4 byte (NOP) instruction, otherwise
    odd bytes are filled with X'00'.

    In days long past, it was usual to put a patch area at the end
    of assembler programs to allow one to patch in instructions later.
    The area would likely be filled with values easy to identify in
    printed storage dumps.

    -- glen
    glen herrmannsfeldt, May 28, 2014
  17. (snip)
    The IBM compilers for OS/360, and I believe successors, generate
    (optionally) a listing file with pseudo-assembler code. It is meant
    to be human readable, but not meant for input to an assembler.
    (Some save paper by doing it in two columns on the page.)
    One advantage of generating assembler code is that it leaves
    resolving the addresses for forward references to the assembler.

    The OS/360 object file format has an address on each record (card)
    such that you could back patch addresses in later cards. It isn't
    usual for compilers to do that, though.

    Another possibility is the in-core compiler like WATFOR and WATFIV
    that write the object code bytes directly where they go. Easy to
    patch forward references that way!
    When I had an actual compiler class (many) years ago, there was an
    example (of what not to do), about a compiler that generated
    assembler code with labels of the form XXXXnnnn where nnnn were
    digits, and an assembler that hashed on the first four characters
    of labels.

    -- glen
    glen herrmannsfeldt, May 28, 2014
  18. Keith Thompson

    Jorgen Grahn Guest

    I was going to say you're mistaken, but I checked and that's indeed
    what gcc does on my system. Interesting.
    This might work worse on certain systems and better on others. Unix
    is, I'm told, optimized for fast fork+exec so it's not surprising that
    the gcc developers chose that design.

    Jorgen Grahn, May 29, 2014
  19. Keith Thompson

    BartC Guest

    It can work faster if a stylised assembler syntax is chosen that is
    efficient to process, especially if it doesn't need to be human readable.
    And gcc's syntax (for x86 at least) is a long way short of being human
    readable; even the simplest bit of code:

    int a,b,c;



    movl 8(%esp), %eax
    movl 12(%esp), %edx
    addl %edx, %eax
    movl %eax, 4(%esp)

    which offset maps to which variable? By contrast, I generate something like

    mov r0, [rframe+b]
    add r0, [rframe+c]
    mov [rframe+a], r0

    Would it have been that difficult for gcc to optionally generate, as well as
    whatever it likes when the assembler source is used internally, a version
    that is easier to follow? (Then it would be far easier to pinch
    code-generating ideas from it.)
    BartC, May 29, 2014
  20. Keith Thompson

    David Brown Guest

    Yes, that's correct. The unix philosophy is that each program should do
    one thing, and do it well, so that you make complex systems by tying
    together small simple programs. Thus the compiler, assembler, linker,
    librarian, disassembler, object code converter, etc., are all separate
    programs. Even within the compiler, the pre-processor is separate from
    the main compiler. This doesn't just apply to gcc - it is common for
    other toolchains from a unix background.

    And while process creation (fork + exec), file handling (especially
    temporary files), and pipes are much less efficient in Windows than on
    *nix systems, they are still fast enough that it is not a major cause of
    compilation time with gcc on Windows. (It is, however, one of the
    reasons why mingw gcc builds are measurably faster than cygwin builds.)
    David Brown, May 29, 2014
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.