In the assembly language used in Unix on DEC machines (PDP-11, VAX), one can use numerical labels and refer to them with suffixes "b" and "f" meaning "backwards" and "forwards", e.g. (in the insructions beql, bneq, aoblss, acbw below)

# search the root directory

    clrl    r12         # init block pointer
    clrl    r5          # use beginning of mem as buffer
    bsbw    lread
    bneq    stask           # eof, try another file
    clrl    r9
    movzwl  (r9)+,r0        # empty entry?
    beql    2f              # yes, skip it
    clrl    r1
1:  cmpb    (r9)[r1],bootname[r1]   # MicroVAX II doesn't have cmpc
    bneq    2f
    aoblss  $DIRSIZ,r1,1b
    brb diryes          # the name we want

2:  acbw    $FSBSIZE-1,$DIRSIZ,r9,0b
    incl    r12         # get next block
    brb dirblk

Numerical labels can be reused as the references signify the closest occurrence of the label with the given number in the given direction (and, IIRC, no further than the closest alphanumerical label in that direction). This greatly reduced the need to invent names for labels, made the assembly code quite neat, and improved readability with the relative location of the branch target explicitly specified.

Per @another-dave in the comments, the original DEC assembly language had local labels as well, with a slightly different naming convention.

Was there a similar feature in the IBM assembly language?

  • 3
    IBM has had rather a number of systems over the years. Which one(s) do you mean? Apr 16, 2021 at 22:50
  • 1
    I don't believe that to be true for Macro-10, Macro-11, or Macro-32. The latter two had 'local labels' of the form nn$, where the 'nn' part had to be unique in the range between any two ordinary labels.. There was no 'f' and 'b' suffix on the reference to the label (no need). The forward-and-back convention was for the Unix PDP-11 assembler. A glance at an arbitrary online copy of the Macro-32 manual confirms my view for VAX.
    – dave
    Apr 16, 2021 at 22:52
  • 1
    @JerryCoffin The earliest for which the question can be answered in the positive. Or, more specifically, IBM/360.
    – Leo B.
    Apr 16, 2021 at 22:59
  • 2
    Oh, a closer look at your code suggests you're using Unix assembler. By $DIRSIZ you evidently mean 'literally the value DIRSIZ'. That would be written #DIRSIZ in the DEC assembly language, and $ is just another valid character in a name.
    – dave
    Apr 16, 2021 at 22:59
  • 1
    @another-dave True, I remember that convention for the PDP-11 assembler, but the code sample I was able to find is clearly for VAX, still it uses the same convention.
    – Leo B.
    Apr 16, 2021 at 23:02

3 Answers 3


Or, more specifically, IBM/360


All labels were always global.

The 360 assembler did not provide such relative labels (*1).


On the other hand it provided procedural macro language (*2), which as well included the ability to generate symbols and thus labels in arbitrary manner. All needed to make them local is using a unique number - which quite handy was supplied by a macro call counter called SYSNDX. So to generate a local label within a macro, simply build one using this. For example:

A&SYSNDX  DS  0H           * Generates a label Annnn with nnnn increasing with each macro call
          ...              * Some code
          BCT R1,A&SYSNDX  * Jump to the label generated before

As said, the 360 macro language is a full figured procedural language, not just a set of random functions, making it possible to create the same effect. One macro to define such 'relative' labels and build a set of lists (*3), one for each 'number' used, to keep track of existing and future labels. Another macro would then be used to generate these jumps. Such a source may look like this:

0        RELAB             * Generating a '0' label
         ...               * Some code
         RELBR  EQ,2F      * Jump forward to the next '2' label
         ...               * Some code
1        RELAB             * The one generating the labels
         ...               * Some code
         RELBR  NE,1B      * Jump back to the last '1' label
         ...               * Some code
2        RELAB             * Generating a '2' label

Looks quite like your example - although, I'd split the forth and back from the label (e.g. write EQ,1,B), but that's up to taste.

The incredible simple and mighty nature of having a procedural macro language and not a bunch of replacement rules opens the range to any concept one can think up. Which in turn would lead to skipping the idea at all, and go straight for structured programming. After all, why bother with labels at all if one can let the Assembler do all low level work?

*1 - And I never missed them in all the decades of /360 programming. Tried them with some toy assemblers, but never really found them useful ... well, maybe if there is nothing else.

Beside that, I'd rather consider them bad idea. Put a new label between two existing (something that happens all the time when editing/extending) and the whole sequence may be screwed.

*2 - Think of macros as scripts written in a simple language. Whenever a macro is invoked, its script is executed. It may generate code, or just manipulate some data structures to be interpreted by a later macro (or macro instructions within the source as well). Every part of the invocation can be read, checked and acted upon as parameters. Noting is done by default. As output any arbitrary source line can be generated - including ofc. macro calls as well :))

*3 - Ok, lists sounds more than it is, in reality one needs to hold only two variables for each of the numbers allowed as label.

  • I knew about &SYSNDX for labels local to a macro invocation (under the name of &SNDX in the BESM-6 parlance), but I was not aware of the features which would allow to implement macros with the functionality you describe. It would be interesting to see how expensive such an implementation with macros would be. Apparently in the UNIX world they have found local labels useful, if they are so common in the code.
    – Leo B.
    Apr 17, 2021 at 3:28
  • @LeoB. Well, the Commodore world found the 1541 useful, but there might be not may preferring it over a less restricted drive. Or the Unix world living with GNU assembler :) I'm not sure what you mean with 'expensive' in case of macros. Regular code is like 1/3rd macros, some of them hundredths of lines (macro instructions, not generated assembly). Not really a show stopper. Here it woudl be maybe a dozend lines each. Having seeped about, I think it might be done in like 3-4 lines when using indirect naming (variables holding variable names :))
    – Raffzahn
    Apr 17, 2021 at 10:57
  • @LeoB.- the DEC '10$' form was useful (and used) in PDP-11/VAX code too. 'Names' tended to get used for meaningful sections of code, not just the 'I happen to need to branch to here' cases, which got local symbols. Made the code a lot more readable, IMO. We of course had the ability to generate names in macro expansion, but who wants to use macros for a simple branch target?
    – dave
    Apr 17, 2021 at 12:23
  • @another-dave Well, I would - and I did. Creating labels is an annoying burden, exactly what the assembler was made for to remove. I guess you can't deny that writing IF cond / comp / THEN / ... / ELSE / ... / BEND is way more convenient than having to think of labels and branches. Let the Assembler do that task and focus on what your program is about. Whenever I had to join a project without such macros, I either installed my own, or wrote some. And yes, I even did so for x86 MASM - long before MS introduced helper functions. Building a nesting stack with MASM macros was an evil task..
    – Raffzahn
    Apr 17, 2021 at 14:20
  • 1
    I was able to write a working RELAB macro and a RELBR macro; with two exceptions: not allowing label 0 because array variables are 1-based, and specifying F or B as a separate argument to the macro for convenience.
    – Leo B.
    Apr 21, 2021 at 20:41

Yes, maybe, in a way. It depends on how you define “IBM assembly language”. If it must be officially produced and distributed by IBM, then this isn't an answer, but if the sole criterion is that it is an assembly language for an IBM computer, then the answer is definitely yes.

The IBM 650 had an assembler called SOAP, for Symbolic Optimum Assembly Program. It was “optimum” in that it tried to assign memory addresses so that the drum movement required for retrieving the next instruction was minimized. A second version of SOAP, SOAP II, was produced and distributed by IBM, becoming the canonical assembler for the 650.

In 1958, Donald Knuth (whose first experiences with a computer were with the 650) created a modification of SOAP II, which he called SOAP III. The manual for SOAP III is available online. This assembler supported local labels, although Knuth's term for them is “program point”.

D. Program points. When either the D- or I-address is to refer to location “1” which appers later, the address “IF” (1 forward) is given; to refer to location “1” which appeared earlier in the program, the address “1B” (1 backward) is given. The location 1 would be called simply “1.” Ten program points are available, 0–9, and each may be constantly redefined during the course of the program. If the program is written down on a coding form in sequence, “1F” will always refer to the next location 1 on the coding form; “3B” will always refer to the previous location "3" occurring on the form. In location 1, a D- or I-address of 1F may be given immediately, referring to a future location 1. A D- or I-address of IB would still refer to the last location 1 (not the present one), A D- or I-address of “1” will refer to its own location.

Rules: 1. A D- or I-address will be interpreted by SOAP as a program point only if the symbolizer part [i.e., the first character of the address] is a number, the second column is a “B” or an “F”, and the remaining columns are blank. 2. The L-address of an instruction will be interpreted by SOAP as a program point only if the symbolizer part is a number and all remaining columns are blank. 3. A D- or I- address consisting of a number followed by four blanks will always refer to its own location, regardless of what number is used or what type of address is used in the L-address.

This is the first implementation of the idea that I am aware of. Here's a listing of SOAP III, so you can see them in action. Knuth would go on to incorporate it into an assembler for the Burroughs 205, and some other Burroughs assemblers had them as well. Most famously program points are available in the assembler for Knuth's MIX computer.

In the SOAP III manual, Knuth credits Melvin Conway with the concept of program points.

  • 1
    Well, despite my initial misconception about the origin of the idea, as DEC had incorporated the notion of local labels in some form in its "official" assembler, I'm still interested in one officially produced and distributed by IBM.
    – Leo B.
    Apr 17, 2021 at 0:29
  • 1
    Thanks for sharing piece of history! I'd never think that those awkward numeric labels and 1B/1F branch designations, still used in assemblers, were invented by Conway and implemented by Knuth in 1958!
    – lvd
    Apr 20, 2021 at 15:48

In a macro language with an IBM-like syntax, local labels are implementable with macros defined approximately as

&a lbl                 define a label
 gblc &b(9),&f(9)
 aif (&f(&a) eq '').n
&b(&a) setc &f(&a)     copy from forward to backward list
 ago .e
.n anop
&b(&a) setc 'm&sysndx' will be backward-referenced, assign a name
.e anop
&b(&a) nop
&lab mbr &insn,&targ,&dir
 gblc &b(9),&f(9)
 aif (&dir ne 'b').f
 aif (&b(&targ) ne '').d
 mnote label &targ not previously defined
 ago .e
.d anop
&lab &insn &b(&targ)
 ago .e
.f aif (&dir eq 'f').ff
 mnote direction must be b or f
 ago .e
.ff aif (&f(&targ) ne '').ff2
&f(&targ) setc 'm&sysndx'
.ff2 anop
&lab &insn &f(&targ)
.e mend

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .