!standard 3.10.2(26/3) 20-06-15 AI12-0363-1/05 !standard 9.10(1/5) !standard C.6(13.3/5) !standard C.6(19.1/5) !class Amendment 20-02-05 !status Amendment 1-2012 20-06-15 !status ARG Approved 10-0-2 20-06-13 !status work item 20-02-05 !status received 19-11-12 !priority Low !difficulty Medium !subject Fixes for Atomic and Volatile !summary We clarify the wording associated with AI12-0128-1 having to do with "rounding up" accesses to nonatomic subcomponents of atomic objects. We introduce a new boolean aspect "Full_Access_Only" which requires any access to any subcomponent to be "rounded up" to the enclosing Full_Access_Only object, and disallows having any "full access" subcomponents. We indicate that the default for Volatile(_Components) and Independent(_Components) is False only if their value is not already determined by a corresponding Atomic(_Components) specification. We try to clean up the places in the wording of C.6 where changes due to multiple partially overlapping AIs have led to inconsistent or unclear wording. !problem (1) C.6(13.3/5) disallows marking a type atomic if it has nonatomic aliased subcomponents, but says nothing about marking an *object* of such type as atomic. This doesn't make much sense, and we can instead solve the problem by preventing taking 'Access of a nonatomic subcomponent of an atomic object, rather than trying to disallow the existence of aliased subcomponents. C.6(13.3/5) also bans independently addressable components of nonatomic subcomponents of an atomic type. This seems unnecessary, and we can again address this by saying such subcomponents cannot be manipulated concurrently. (Net effect is we can eliminate the last two sentences of this paragraph, and handle these issues elsewhere.) (2) We have said that nonatomic subcomponents of an atomic object, when read or written, will have their access "rounded up" to the nearest enclosing atomic object (with a "write" to such a subcomponent typically performed as an atomic read followed by an atomic write of the enclosing atomic object). However, what about atomic subcomponents of atomic objects? It would be incompatible to disallow them completely. Should we provide a separate aspect for specifying that no atomic subcomponents are permitted of a given atomic type? (Yes). (3) It is not completely clear whether, in Ada 202X, an assignment to a nonatomic subcomponent of an atomic object is considered to be a single "action," or a sequence of two (sequential) actions, the first an atomic read and the second an atomic write. (It should be clarified that they are treated as two sequential actions.) (4) We disallow having a volatile type used as an actual for a generic formal array type, unless the element type of the formal type is volatile. Why is that, and why doesn't it mention atomic? (We clarify and fix a hole in the rule.) (5) The current wording implies that Volatile and Independent default to False if not specified. But if Atomic is specified True, we want Volatile and Independent to default to True (in fact, they are True, and that should only allow to be confirmed). We should similarly say that Volatile_Components is essentially equivalent to Volatile, and that we provide both merely to be consistent with Atomic_Components and Independent_Components. (We fix the wording.) (6) Overall, C.6 has been updated by various different AIs. It would be good to scrub the wording to make sure it is consistent and clear. (We try to do that in this AI.) !proposal We move the check for not taking 'Access of an aliased subcomponents of an atomic object to 3.10.2. We move to 9.10 the rule that says independently addressable, but nonatomic subcomponents of an atomic object are *not* safely manipulatable concurrently. We add a Full_Access_Only aspect which can be applied to atomic and volatile types and objects to indicate that no atomic (or full access) objects are permitted as subcomponents. We refine the rule relating to generic formal array types, to clarify its purpose and fix a hole relating to formal array types with aliased components. We fix the wording on Volatile and Independent so they properly are True if Atomic is specified True. Ditto for Volatile/Independent_Components. We make Volatile and Volatile_Components imply one another, since they are effectively equivalent (unlike Atomic_Components and Independent_Components). !wording Modify 3.10.2 (26/3): * The view shall not be a subcomponent that depends on discriminants of an object unless the object is known to be constrained{; * The view shall not be a nonatomic subcomponent of a full access object (see C.6)}. Modify 9.10 (1/5): If two different objects, including nonoverlapping parts of the same object, are independently addressable, they can be manipulated concurrently by two different logical threads of control without synchronization{, unless both are subcomponents of the same full access object, and either is nonatomic (see C.6)}. Add after C.6(6.4/3): Full_Access_Only The type of aspect Full_Access_Only is Boolean. Modify C.6(6.10/3): If any of these aspects are directly specified, the aspect_definition shall be a static expression. If not specified {for a type} (including by inheritance), [each of these]{the Atomic, Atomic_Components, and Full_Access_Only} aspects [is]{are} False. {If any of these aspects are specified True for a type, then the corresponding aspect is True for all objects of the type. If the Atomic aspect is specified True, then the aspects Volatile, Independent, and Volatile_Component (if defined) are True; if the Atomic_Components aspect is specified True, then the aspects Volatile, Volatile_Components, and Independent_Components are True. If the Volatile aspect is specified True, then the Volatile_Components aspect (if defined) is True, and vice versa. When not determined by one of the other aspects, or for an object by its type, the Volatile, Volatile_Components, Independent, and Independent_Components aspects are False.} {AARM Ramification: Aspects Volatile and Volatile_Components (when defined) are equivalent. We provide the Volatile_Components aspect only to give symmetry with Atomic_Components and Independent_Components aspects.} Add after C.6(8.1/4): The Full_Access_Only aspect shall not be specified unless the associated type or object is volatile [Redundant:(or atomic)]. A /full access/ type is any atomic type, or a volatile type for which the aspect Full_Access_Only is True. A /full access/ object (including a component) is any atomic object, or a volatile object for which the aspect Full_Access_Only is True for the object [Redundant: or its type]. A Full_Access_Only aspect is illegal if any subcomponent of the object or type is a full access object or is of a generic formal type. AARM Ramification: This last rule breaks privacy, but that is considered OK for representation clauses when there is no clear alternative. Note that atomic objects may be nested, so long as the outer atomic object does not have the Full_Access_Only aspect True. AARM Reason: We disallow subcomponents of a generic formal type in a Full_Access_Only object or type as the actual to a formal type can be a full access type. We could have had a less restrictive rule, but such a use is unlikely as full access only objects are intended to be used to access memory-mapped devices with access restrictions, and those will need a concrete mapping not possible for generic formal types. Modify C.6(12/5): If an atomic object is passed as a parameter, then the formal parameter shall either have an atomic type or allow pass by copy. If an atomic object is used as an actual for a generic formal object of mode in out, then the type of the generic formal object shall be atomic. If the prefix of an attribute_reference for an Access attribute denotes an atomic object [Redundant: (including a component)], then the designated type of the resulting access type shall be atomic. Corresponding rules apply to volatile objects{ and to full access objects.} Replace C.6(12.1/5): If the Atomic, Atomic_Components, Volatile, Volatile_Components, Independent, or Independent_Components aspect is True for a generic formal type, then that aspect shall be True for the actual type. If a volatile type is used as an actual for a generic formal array type, then the element type of the formal type shall be volatile. If an atomic type is used as an actual for a generic formal derived type, then the ancestor of the formal type shall be atomic. A corresponding rule applies to volatile types and similarly to full access types. with: If the Atomic, Atomic_Components, Volatile, Volatile_Components, Independent, Independent_Components, or Full_Access_Only aspect is True for a generic formal type, then that aspect shall be True for the actual type. If an atomic type is used as an actual for a generic formal derived type, then the ancestor of the formal type shall be atomic. A corresponding rule applies to volatile types. If a type with volatile components is used as an actual for a generic formal array type, then the components of the formal type shall be volatile. Furthermore, if the actual type has atomic components and the formal array type has aliased components, then the components of the formal array type shall also be atomic. A corresponding rule applies when the actual type has volatile full access components. AARM Reason: The limitations on formal array types are separate for volatile and atomic because of the fact that only volatility is carried down to all subcomponents of a volatile object, while atomicity is not. The goal of both limitations is that we don't want 'Access for an access type to produce a value that designates an object whose atomicity and volatility don't agree with that of the designated type of the access type. The above rules ensure that the generic "sees" the relevant volatility and atomicity. [Author's note: The rule on volatility could be relaxed somewhat, to require that only the aliased, tagged, or private subcomponents of the formal type are volatile, but the current rule has been in place since 2012, so we don't see the need to relax it at this point.] Modify C.6(13.3/5): If a nonatomic subcomponent of [an atomic]{a full access} object is passed as {an}[the] actual parameter in a call then the formal parameter shall allow pass by copy (and, at run time, the parameter shall be passed by copy). A nonatomic subcomponent of [an atomic]{a full access} object shall not be used as an actual for a generic formal of mode in out. [A nonatomic subcomponent of an atomic type shall not be aliased. A nonatomic subcomponent of an atomic type or object shall not have components that are specified to be independently addressable.] Modify RM C.6(19.1/5): All reads of or writes to any nonatomic subcomponent of [an atomic]{a full access} object are performed by reading and/or writing all of the nearest enclosing [atomic]{full access} object. Add after AARM C.6(19.1.c/5): AARM Discussion: The atomic reads and writes associated with accesses to nonatomic components of a full access object that is atomic are normal atomic operations -- all of the rules that apply to other atomic operations apply to these as well. In particular, these atomic reads and writes are sequential if they apply to the same object. !discussion (1) This can happen in cases like: type R is record X : aliased Integer; end record; Z : R with Atomic; -- Legal? (Yes.) P : access Integer := Z.X'Access -- Legal? (No.) We permit the type declaration, but disallow the 'Access. This is necessary in general to avoid breaking privacy, since we might actually have an aliased component of a component of R, where the component of R is of a private type. For the issue of independent addressability, we remove the last sentence of (13.3/5), and change the rules associated with independent addressability in 9.10 to say that threads can safely manipulate independently addressable components, unless they are subcomponents of the same full access object, and either is nonatomic. This also prevents breaking privacy. The existing sentence could require breaking privacy in certain cases. (2) We add the Full_Access_Only aspect to control whether nesting of atomic objects is permitted. The GNAT aspect Volatile_Full_Access is the inspiration for this, but it does not actually imply everything that "atomic" implies, so we allow it to be specified as an addition to *either* atomic or volatile. Volatile + Full_Access_Only is intended to match GNAT's existing pragma. Atomic + Full_Access_Only is essentially equivalent to simply "Atomic" but with the added legality rule that Full_Access_Only disallows having atomic (or full access) subcomponents. This *does* break privacy, but we could not figure out anyway to avoid it, and since it is a representation aspect, it is something that could be "bubbled up" and across private types. (3) The AARM note C.6(19.1.c/5) describes the intent that a read-modify-write cycle is expected and that these are separate atomic reads and writes. As such, there are two operations and these are sequential with respect to any other operations on the same object. (4) We went back to the original AI that introduced the restriction on generic formal array types, and discovered a hole having to do with formal arrays with aliased components, which only applies to atomic. So we move the formal array restriction into its own paragraph, handle both volatile and atomic special cases, and add an AARM note to explain what is behind the rules. (5) There were words that indicated that all atomic objects are also volatile, but the defaulting to False of Volatile when not specified seemed to contradict that. Similar considerations apply to Independent, and the *_Components aspects. So we changed the rules and made the defaults match the semantics of atomic implying volatile and independent. Finally, Volatile and Volatile_Components are equivalent, so we make that explicit, and have one imply the other. (6) We completely replace (12.1/5) because it had been edited so often that it didn't seem to end up in a logical sequence. Hopefully the new wording is a little clearer. !ASIS No changes needed. !ACATS test ACATS B-Tests for Annex C might need some changes to add or remove cases, but the changes aren't enough to need new tests. !appendix From: Tucker Taft Sent: Tuesday, November 12, 2019 3:39 PM It is not completely clear whether, in Ada 202X, an assignment to a nonatomic subcomponent of an atomic object is considered to be a single "action," or a sequence of two (sequential) actions, the first an atomic read and the second an atomic write. In other words, when two separate, concurrent tasks each update a nonatomic subcomponent of the same object, is the execution erroneous? Task1: Atom.X := 1; Task2: Atom.Y := 3; There is certainly a race condition, but a race condition between atomic actions is not erroneous. Another way to think about this, is should this be treated as equivalent to: Task1: Atom2 := Atom2 + 3; Task2: Atom2 := Atom2 - 5; The above is not erroneous, as it is a sequence of atomic (sequential) actions. My instinct would be to consider an update to a nonatomic subcomponent of an atomic object to be a sequence of two atomic actions, since we have now defined the semantics that way. As with the example of concurrent updates to an atomic object, there are a well-defined set of possible outcomes, so the situation is not unbounded. We could declare it to be a bounded error, but we haven't declared the second example to be a bounded error, so I would rather not burden the programmer with a concern that a Program_Error might come flying out some of the time. Thoughts? **************************************************************** From: Randy Brukardt Sent: Tuesday, November 26, 2019 8:52 PM > It is not completely clear whether, in Ada 202X, an assignment to a > nonatomic subcomponent of an atomic object is considered to be a > single "action," or a sequence of two > (sequential) actions, the first an atomic read and the second > an atomic write. In other words, when two separate, > concurrent tasks each update a nonatomic subcomponent of the same > object, is the execution erroneous? Could you explain why you say this? ... > My instinct would be to consider an update to a nonatomic subcomponent > of an atomic object to be a sequence of two atomic actions, since we > have now defined the semantics that way. This seems to me to be the answer: we have defined the semantics to be two atomic actions. I can hardly imagine atomic actions that aren't sequential, so I don't understand the question. > As with the example of concurrent updates to an atomic object, there > are a well-defined set of possible outcomes, so the situation is not > unbounded. We could declare it to be a bounded error, but we haven't > declared the second example to be a bounded error, so I would rather > not burden the programmer with a concern that a Program_Error might > come flying out some of the time. > > Thoughts? I don't know why you think the semantics aren't clear here. I don't know how much clearer C.6(19.1/5) could be. The rewrite of C.6(20/5) and the associated AARM note 20.j/5 make it clear that read-modify-write code is intended. And C.6 has no atomic update operations (that's the Brad library routines in Ada 202x). What other semantics could there be? As you noted, Atomic updates are never safe in Ada (outside of the new libraries), and they need to be avoided if multiple writers are possible. No reason for components to be different. **************************************************************** From: Tucker Taft Sent: Wednesday, November 27, 2019 7:55 AM > This seems to me to be the answer: we have defined the semantics to be two > atomic actions. I can hardly imagine atomic actions that aren't sequential, > so I don't understand the question. Fair enough. I just think we should clarify this somewhere -- AARM note to clarify the intent might be adequate. **************************************************************** From: Tucker Taft Sent: Thursday, November 14, 2019 10:22 AM Eric Botcazou, the AdaCore engineer implementing AI12-0128, suggested some refinements to the wording. In particular, given this paragraph from C.6 (13.3/5): "If a nonatomic subcomponent of an atomic object is passed as the actual parameter in a call then the formal parameter shall allow pass by copy (and, at run time, the parameter shall be passed by copy). A nonatomic subcomponent of an atomic object shall not be used as an actual for a generic formal of mode in out. A nonatomic subcomponent of an atomic type shall not be aliased. A nonatomic subcomponent of an atomic type or object shall not have components that are specified to be independently addressable." He suggests the following additions (in {...}). "If a nonatomic subcomponent of an atomic object is passed as the actual parameter in a call then the formal parameter shall allow pass by copy (and, at run time, the parameter shall be passed by copy). A nonatomic subcomponent of an atomic object shall not be used as an actual for a generic formal of mode in out. A nonatomic subcomponent of an atomic type {or object} shall not be aliased. A nonatomic subcomponent of an atomic type or object shall not have {nonatomic} {sub}components that are specified to be independently addressable." I am unsure why we didn't say "type or object" systematically in the above, nor why we didn't say "subcomponents" rather than "components" systematically. And in the last sentence, specifying something to be atomic is effectively specifying it to be independently addressable, so it seems better to only worry about nonatomic subcomponents. Any comments or rationale for some of the subtle differences between the four rules? > Begin forwarded message: > > From: Eric Botcazou > Subject: Re: [SB07-048] - Ada2020: AI12-0128 Exact size access to > parts of composite atomic objects #999 > Date: November 14, 2019 at 8:09:14 AM EST > To: "Tucker Taft @ adacore" > Cc: Bob Duff > >> Interesting. The author of the AI (I believe Steve Baird) was >> clearly making a distinction, but I agree that if a type has a >> (non-atomic) aliased (sub)component, you probably don't want to allow >> an object of the type to be marked atomic. > > Right, so I'm going to implement this in the compiler: > > "A nonatomic subcomponent of an atomic type or object shall not be aliased." > >>> The fourth rule seems a bit too strong since, as per C.6 (8 1/3), >>> atomic objects are considered to be specified as independently >>> addressable, but atomic components of a nonatomic subcomponent of an >>> atomic type or object don't seem to be problematic for the new semantics >>> since nothing changes. >> >> Good point -- the fourth rule should probably say: >> A nonatomic subcomponent of an atomic type or object shall not have >> *nonatomic* components that are specified to be independently addressable. > > In the end, I think that it's essentially equivalent to: > > "A nonatomic subcomponent of an atomic type or object shall not be > specified to be independently addressable." > > and I'm going to implement this in the compiler. **************************************************************** From: Randy Brukardt Sent: Thursday, November 14, 2019 2:32 PM A nonatomic subcomponent of an atomic object is necessarily Volatile (unlike atomicity, volatility applies to components, and all atomic components), so the rules in C.6(12/5) apply to such components. C.6(13.3/5) is just a tightening of the rules for nonatomic subcomponents; not everything needs to be covered. Specific comments: "A nonatomic subcomponent of an atomic type {or object} shall not be aliased." How would a subcomponent of an atomic object be aliased if the type doesn't declare it that way?? This is a check on a type definition, exclusively. (It seems to be a privacy breaking check, however, which is annoying, but that doesn't seem important given the unlikelyness of nonatomic components of a private type -- these things tend to be integers or enumerations.) "A nonatomic subcomponent of an atomic type or object shall not have {nonatomic} {sub}components that are specified to be independently addressable." I think this rule is (implicitly) banning any atomic (sub)components of a nonatomic subcomponent; how would that work? The nonatomic subcomponents would prevent the inner things from being treated as atomic in some contexts, so that is nonsense. If some component is atomic, then the enclosing type also has to be atomic, and so on. The proposed change would allow that sort of nonsense. The AI discussion makes it clear that no nonatomic component of an atomic object should *ever* be independently addressable. It makes no sense for some subcomponent of a non-independently addressable object to be itself independently addressable. Ergo, this second change is certainly a bad idea. **************************************************************** From: Tucker Taft Sent: Thursday, November 14, 2019 2:43 PM ... > How would a subcomponent of an atomic object be aliased if the type > doesn't declare it that way?? This is a check on a type definition, > exclusively. (It seems to be a privacy breaking check, however, which > is annoying, but that doesn't seem important given the unlikelyness of > nonatomic components of a private type -- these things tend to be > integers or enumerations.) But what about: type R is record X : aliased Integer; end record; Z : R with Atomic; -- Is this legal? > "A nonatomic subcomponent of an atomic type or object shall not have > {nonatomic} {sub}components that are specified to be independently > addressable." > > I think this rule is (implicitly) banning any atomic (sub)components > of a nonatomic subcomponent; how would that work? I don't follow. Perhaps the existing rule implied that, but with the addition of "nonatomic" here, we are removing any restriction on atomic subcomponents. Or am I missing your point? > The nonatomic subcomponents > would prevent the inner things from being treated as atomic in some > contexts, so that is nonsense. If some component is atomic, then the > enclosing type also has to be atomic, and so on. > > The proposed change would allow that sort of nonsense. Sorry, I have lost you. Can you give an example. > The AI discussion makes it clear that no nonatomic component of an > atomic object should *ever* be independently addressable. It makes no > sense for some subcomponent of a non-independently addressable object > to be itself independently addressable. Ergo, this second change is > certainly a bad idea. I have really lost you now. Hopefully an example will clarify... **************************************************************** From: Randy Brukardt Sent: Thursday, November 14, 2019 3:13 PM > But what about: > > type R is record > X : aliased Integer; > end record; > > Z : R with Atomic; -- Is this legal? I forgot that is legal. (Not really sure *why* it should be legal to have an atomic object of a nonatomic type, but never mind.) The privacy breaking implied here bothers me in any case. No fix seems possible, however (short of banning nonatomic components of partial views). > > "A nonatomic subcomponent of an atomic type or object shall not have > > {nonatomic} {sub}components that are specified to be independently > > addressable." > > > > I think this rule is (implicitly) banning any atomic (sub)components > > of a nonatomic subcomponent; how would that work? > > I don't follow. Perhaps the existing rule implied that, but with the > addition of "nonatomic" here, we are removing any restriction on > atomic subcomponents. Or am I missing your point? My point is that we WANT to ban such components. They don't make any sense. > > The nonatomic subcomponents > > would prevent the inner things from being treated as atomic in some > > contexts, so that is nonsense. If some component is atomic, then the > > enclosing type also has to be atomic, and so on. > > > > The proposed change would allow that sort of nonsense. > > Sorry, I have lost you. Can you give an example. Sure. See below. > > The AI discussion makes it clear that no nonatomic component of an > > atomic object should *ever* be independently addressable. It makes > > no sense for some subcomponent of a non-independently addressable > > object to be itself independently addressable. Ergo, this second > > change is certainly a bad idea. > > I have really lost you now. Hopefully an example will clarify... For something to be independently addressable, the thing it is a component of has to be independently addressable. Otherwise, one runs into nonsense. In this case, a nonatomic component is explicitly NOT independently addressable. So consider the case you are trying to allow: type Byte_Array is (1..2) of Byte with Atomic_Components; -- Byte_Array is not atomic itself. type Dev_Reg is record Status : Flags; Data : Byte_Array; Padding : Byte; end record with Atomic; -- Rep. clause here, not relevant to the point. My_Reg : Dev_Reg; Here, we've said that the nonatomic components Status, Data, and Padding of an atomic type/object are not independently addressable. For instance, My_Reg.Status := Status; does an atomic access to the entire My_Reg object. That clearly applies to: My_Reg.Data := (2, 2); But the declaration of Data implies that: My_Reg.Data(1) := 2; My_Reg.Data(2) := 2; *is* independently addressable. That is, each of these components is written atomically. But for that to be the case, Data itself has to also be independently addressable. We have a contradiction, and in particular the guarantee provided by making the type Dev_Reg atomic is violated. Again, it's nonsense to say that some component is independently addressable when the enclosing type is not independently addressable. Usually, we simply propagate that information upwards (the enclosing type is automatically independently addressable if any components require that). But in this case, we can't do that. So far as I'm aware, this is the only case where one can turn *off* independent addressability, thus we need a special rule in that case. **************************************************************** From: Randy Brukardt Sent: Thursday, November 14, 2019 7:37 PM Note that independent addressability itself is a guarantee; compilers are usually free to provide it anywhere. But C.6(19.1/5) says not in this case, so you get the contradiction. I note that there also is a usability concern in allowing "deep" atomic subcomponents. If someone is writing a type for which they are depending on C.6(19.1/5) for correct operation, they don't want to have to check the entire text of their program to ensure that it is true. That is, any atomic components should be visible in the declaration of the composite type, so that reading the type declaration and at most the declarations of the component types (to see if any are declared Atomic) can ensure that every read and write uses the entire hardware register. If one allows Atomic subcomponents in nonatomic components of an atomic type, then one has to read the details of every involved type to ensure this property. That's a maintenance hazard waiting to happen (there's too many places that would need a comment that "this type must not be declared atomic"). Note that you can still have atomic subcomponents, it's just necessary for Byte_Array itself to be declared atomic in order for that to be allowed (in which case C.6(19.1/5) clearly does not apply to the component, so no one would be surprised when it does not apply to some part of the component). **************************************************************** From: Tucker Taft Sent: Thursday, November 14, 2019 10:13 PM I'll admit to have lost track of what you are trying to say. I had suggested we alter the fourth rule from: A nonatomic subcomponent of an atomic type or object shall not have components that are specified to be independently addressable. to: A nonatomic subcomponent of an atomic type or object shall not have nonatomic subcomponents that are specified to be independently addressable. What are you proposing the rule should be? **************************************************************** From: Randy Brukardt Sent: Thursday, November 14, 2019 10:27 PM Either the original wording, or perhaps better: A nonatomic subcomponent of an atomic type or object shall not have {sub}components that are specified to be independently addressable. AARM Ramification: a subcomponent that is specified to be atomic is considered to also specify that is independently addressable. [This follows from C.6(8.1/4), which says this literally.] A nonatomic component should not be allowed to have atomic or independently addressable subcomponents, as that is a contradiction (the nonatomic component is defined to be *not* independently addressable, and that has to apply to any subparts as well). If you do not believe the above, then you should simply suggest deleting the rule entirely (whether something is declared atomic has no bearing on whether it can be independently addressable). Note that there is no problem having atomic components of atomic types/objects; it just doesn't make sense to have nonatomic components sandwiched in between. **************************************************************** From: Brad Moore Sent: Wednesday, March 11, 2020 1:12 PM [Part of a larger message, just the relevant part here - Editor.] Also, I had a typo in AI12-0363-1, that I didn't mention in our phone meeting. AARM Ramification: 1st sentence, "a subcomponent that is specified to be atomic is considered to also specify that {it} is independently addressable." **************************************************************** From: Tucker Taft Sent: Wednesday, March 11, 2020 4:37 PM [Part of a larger message, just the relevant part here - Editor.] ... > Also, I had a typo in AI12-0363-1, that I didn't mention in our phone meeting. > > AARM Ramification: 1st sentence, > "a subcomponent that is specified to be atomic is considered to also > specify that {it} is independently addressable." Good catch. **************************************************************** From: Randy Brukardt Sent: Monday, April 27, 2020 10:00 PM AI12-0128-1 enhanced the rules for Atomic such that it can be used to guarentee that only exact size access is used to access subcomponents of an atomic composite object. Recently, a request was made to eliminate some of the requirements of that AI in order to allow more uses of atomic subcomponents. These can be seen in the attached AI. I believe that the changes suggested introduce a major maintenance hazard for anyone that requires exact size access to some hardware. I'm posting here in order to give people who are more likely to have that sort of requirement than me to weigh in on this issue. The problem as I see it is that to get exact size access to a hardware register, one has to ensure that no subcomponent has an atomic subtype or is directly declared atomic. If all of the types involved are declared together, that's not hard to ensure, but anyone can change a declaration and break the property. The compiler will not care, since there is no legality rule that is broken -- just the generated code will change. But of course the generated code will not work if some smaller access is generated. If of course some of the types are declared in other units (even something as simple as a status enumeration), then the verification gets even harder. Even though the AI worries about breaking privacy, that is irrelevant here as representation aspects always break privacy (and Atomic is a representation aspect). Of course, a need for exact size access is fairly rare, and having rules that prevent certain types of subcomponents are likely to get in the way of particular uses. Thus, it might be better to have an aspect to require only exact size access, and reject any declaration that would violate that. Otherwise, it seems to me that any exact size requirement is only going to be followed until the first ham-handed maintainer. Anyone have any informed thoughts on this topic? P.S. The next ARG meeting is Wednesday, so a quick response would be appreciated. If it needs more discussion we can remove it from the agenda of that meeting. **************************************************************** From: Randy Brukardt Sent: Tuesday, April 28, 2020 5:51 PM >I am confused, Randy. As far as I can tell this AI isn't relaxing the >requirements. It is simply moving them to elsewhere in the standard. Can you clarify this? Allowing atomic SUBcomponents makes the bad situation of AI12-0128-1 much worse vis-a-vis the original problem of that AI. If one requires exact size access to some external device, you have no way to guarantee that is the case short of inspecting every subcomponent type to see if anything is declared atomic. Since types can be declared atomic, and that can be added after the fact, one has a substantial maintenance hazard. Recall that the original problem occurs rarely but is critical when it happens. What sometimes happens is that there is a memory-mapped device that only responds to 32-bit read and write cycles (and not to 8-bit read/write cycles). [I'm told this is a real problem in practice, I have no direct experience with it myself, which is why I posted this here for public comment from those with more experience.] We want to be able to use Ada bit-mapped records to access this device, rather than have to do bit masking operations to get at the fields. But if the Ada compiler generates an 8-bit atomic operation, the device will not respond and the program will obviously fail. We partially solved this problem in AI12-0128-1 by mandating that atomic objects with nonatomic components are accessed exactly and one never reads/writes the components individually. But this is a maintenance hazard, since if someone declares one of the components atomic, the device will stop responding and the code will presumably malfunction. I went along with the original rules because there didn't seem to be a taste for a separate aspect and because only the top-level components were allowed to be atomic. That meant that one could pretty much inspect and heavily comment those components, and maintenance problems would be fairly minimal (but not eliminated, as shown below). However, with the elimination of that rule, now any components that themselves are composite also would have to be inspected. It would be best if a program that needs exact-size access but is not going to get it gets rejected at compile-time. Otherwise, the code will silently fail as the hardware restrictions are not followed and some reads and writes simply do nothing (or even read bus noise). Avoiding that would pretty much mean that every type used in such a register access has to be declared locally; there can't be any reuse as that opens the danger of someone changing it in the future and destroying code that they don't even know about. For instance, imagine the following package exists somewhere in the system owned by another group: package Register_Info is type Status is (Ready, Full, Empty, Unplugged, Error); for Status use (....); type Data is mod 2**8; end Register_Info; We need to build access to a 32-bit memory mapped register, and the types above are what we need. So we use them so we can better interface with the rest of the system: with Register_Info; use Register_Info; package My_Device is type Device_Register is record Input_Status, Output_Status : Status; Input_Data, Output_Data : Data; Empty : Data; end record with Atomic; -- Record rep. clause here. The_Device : Device_Register with Address => ...; end My_Device; With the rule changes of AI12-0128-1, this does what we want. However, it is fragile, because a change in the declaration of either of the imported types will break this declaration such that the uses of The_Device will silently malfunction. Remember that I suggested that the type package belongs to another group. Perhaps that group decides that the data needs to be Atomic so that they can get rid of some error messages from parallel. (Never mind that is a bad idea, people do stuff like this all of the time.) If they do that, The_Device will no longer work, but it still will compile. If this is maintenance in a long-running system, it could take a long time before the maintainers will notice that the size guarantee has been lost. I realize that the rules as given in AI12-0128-1 are limiting for uses that don't care about exact size sementics (which will be most of them). So my suggestion is to allow the rules to be weakened back to almost no restrictions on subcomponents (I don't see the point of distributing this Atomic annex special case all over the Standard - it seems to violate our rules about keeping the annexes separate from the core), but in addition to add an Use_Exact_Size_Only aspect that can be given on an atomic type or object. If the new aspect is True, there cannot be any atomic or aliased subcomponents (since those would necessarily violate the needed behavior). This would more directly meet the need and eliminate any maintenance hazards. (Note: We'd still require any atomic object, even without the aspect, to write the exact bits if there are no components that require otherwise. We do want to do that expected thing by default; it's just that we can't allow some ham-handed maintainer to break that if it is in fact an actual hardware requirement.) Note that I don't see any reason to care about privacy for Atomic's rules. These are representation aspects and those never care about privacy. Moreover, I don't think people are using many private types in hardware interfacing. So the concern is more theoretical than real. I'm suggesting using fewer restrictions anyway, unless one gives the new aspect, and that should only be used in the relatively rare case of memory mapped hardware that doesn't work with all sizes. **************************************************************** From: Tucker Taft Sent: Tuesday, April 28, 2020 7:41 PM I think the fundamental question is what do you do when you have nested atomic objects: 1) Make it illegal 2) Ignore the "atomic" on the components, and only obey atomicity on the outermost atomic object. For everything else you "round up" the load/store size to match the *outermost* enclosing atomic object. 3) Obey atomicity at each level. From a nonatomic subcomponent, round up to the *nearest* enclosing atomic object. I think each of these are reasonable, depending on the application. Unfortunately, we have to pick one. I would argue (3) gives the programmer the most flexibility, since if they actually have a hardware register that allows 32-bit reads, as well as 8-bit reads, they can do it with (3) but not with any of the others. If the hardware doesn't allow 8-bit reads, then the programmer should not nest an atomic object that is 8-bits in size inside the 32-bit atomic object. And as you say private types are not that common in such programming, so the programmer can see exactly what they are doing. Anyone who is writing a device driver is probably looking very carefully at what types they are using. So for me, (3) gives the programmer the most control, and is to some extent a superset of the other capabilities. I vaguely remember my days of directly addressing hardware registers, and I believe there were some that allowed both word and byte access, though who knows if that is a real memory, or whether it still applies now. But the fundamental point is (3) gives the programmer the ability to choose what they want to do. It seems that either approach could introduce a bug if the programmer doesn't know what they are doing, or if some change is made in maintenance. But for hardware device drivers, my sense is you want to give control, more than you want to somehow protect the programmer from themself. **************************************************************** From: Randy Brukardt Sent: Tuesday, April 28, 2020 9:24 PM >I think the fundamental question is what do you do when you have nested atomic >objects: > >1) Make it illegal > >2) Ignore the "atomic" on the components, and only obey atomicity on the > outermost atomic object. For everything else you "round up" the load/store > size to match the *outermost* enclosing atomic object. > >3) Obey atomicity at each level. From a nonatomic subcomponent, round up to > the *nearest* enclosing atomic object. > >I think each of these are reasonable, depending on the application. >Unfortunately, we have to pick one. Why do we have to pick only one? The usual case of atomic objects and this particular hardware case are very different and there is a much different requirement in the latter case. While in the normal case, the exact rule doesn't matter and the flexibility seems valuable. I think it is absolutely necessary for the programmer to tell the compiler when this exact size case is required. Then the compiler can (and has to) reject anything that doesn't meet the exact size requirements. Anything else is very unsafe, since the code can silently get the wrong answer (since the hardware will not be read). >... >I vaguely remember my days of directly addressing hardware registers, and I >believe there were some that allowed both word and byte access, though who >knows if that is a real memory, or whether it still applies now. I made this point when the subject originally came up, and I was told by a number of people that hardware with these sorts of limitations shows up rather regularly. A lot of these registers are byte addressed anyway (no larger access), so the problem probably doesn't really show up with those. Anyway, the reason I posted here was to get some feedback from real embedded practioners as to whether this is common enough to have a special aspect to declare. Silence is a form of an answer, I guess, but we haven't given them enough time. > It seems that either approach could introduce a bug if the programmer doesn't > know what they are doing, or if some change is made in maintenance. But for > hardware device drivers, my sense is you want to give control, more than you > want to somehow protect the programmer from themself. It's not themself, it's the guy that comes along 5 years from now and knows nothing about exact size accesses and little about atomics. You are essentially saying that if there is any such exact size access required, you have to write it completely self-contained, comment it heavily, and then use other types to communicate the results to the rest of the program. There's no possibility of reuse or sharing because to have those things greatly increases the chance of a change. And the program will silently malfunction if it wrong -- no errors, no traps, nothing. That to me is the C approach -- leave it up to the programmer to do the right thing - and if they don't, too bad for them (and the people flying on their airplane). If we're not going to solve the original problem at all, then AI12-0128-1 was a complete waste of time. Any reasonable compiler would have done the right thing most of the time anyway, and since this stuff is untestable, it's highly likely that will remain the case. At least having a testable aspect would clue implementors into the importance of the issue. Hopefully some embedded programmers will comment. **************************************************************** From: Jeff Cousins Sent: Wednesday, April 29, 2020 3:11 AM ... >> I vaguely remember my days of directly addressing hardware registers, and I >>believe there were some that allowed both word and byte access, though who >>knows if that is a real memory, or whether it still applies now. >I made this point when the subject originally came up, and I was told by a >number of people that hardware with these sorts of limitations shows up >rather regularly. A lot of these registers are byte addressed anyway (no >larger access), so the problem probably doesn't really show up with those. Stretching the memory cells here, but at one time I wrote most of the device drivers in the company, and I think you’re correct (i.e. such fussy h/w can exist) . **************************************************************** From: Simon Wright Sent: Wednesday, April 29, 2020 6:44 AM I just checked briefly through the first few peripherals' register descriptions in the STM32F4xxx Reference Manual issue 11. Some of them leave it open (I'd guess at 32 bit-only), but the ADC says "The peripheral registers must be written at word level (32 bits). Read accesses can be done by bytes (8 bits), half-words (16 bits) or words (32 bits)." whereas the DAC says 32 bit only. DCMI says "All DCMI registers have to be accessed as 32-bit words, otherwise a bus error occurs." which would at least reduce the chance of missing a programming error. TIM1 & TIM8 say "The peripheral registers must be written by half-words (16 bits) or words (32 bits). Read accesses can be done by bytes (8 bits), half-word (16 bits) or words (32 bits)." **************************************************************** From: Tucker Taft Sent: Wednesday, April 29, 2020 7:08 AM Another issue worth considering is that Ada 2012 semantics correspond to (3) for nested atomic objects. Ada 2012 makes no requirement about "rounding up" of nonatomic subcomponents of an atomic object. But for nested atomic objects, there is no rounding up in Ada 2012. So we should think about whether compatibility should be a relevant concern in this decision. **************************************************************** From: Tucker Taft Sent: Wednesday, April 29, 2020 7:25 AM >>I think the fundamental question is what do you do when you have nested atomic >>objects: >> >>1) Make it illegal >> >>2) Ignore the "atomic" on the components, and only obey atomicity on the >> outermost atomic object. For everything else you "round up" the load/store size to match the *outermost* enclosing atomic object. >> >>3) Obey atomicity at each level. From a nonatomic subcomponent, round up to >> the *nearest* enclosing atomic object. >> >>I think each of these are reasonable, depending on the application. >>Unfortunately, we have to pick one. >Why do we have to pick only one? The usual case of atomic objects and this >particular hardware case are very different and there is a much different >requirement in the latter case. While in the normal case, the exact rule >doesn't matter and the flexibility seems valuable. > >I think it is absolutely necessary for the programmer to tell the compiler when >this exact size case is required. Then the compiler can (and has to) reject >anything that doesn't meet the exact size requirements. Anything else is very >unsafe, since the code can silently get the wrong answer (since the hardware >will not be read). ... AdaCore has a separate aspect (Volatile_Full_Access -- VFA) but I believe we chose instead to make "atomic" work the "right way." The reason is that you still need to address what happens when Atomic objects occur inside of a VFA object, and vice-versa. So I don't think having a separate aspect really addresses the concern. And based on responses to this, it seems that both sort of situations come up, and almost certainly all of them want rounding up of *non* atomic subcomponents, so it all comes down to whether atomicity is lost if you have an atomic object nested inside something that requires rounding-up. I am not seeing a real need to override the atomicicity of a nested object, at least not in all cases. What we could add is an aspect that *disallows* nested atomic objects. So we would could support both (1) and (3), depending on the state of this additional aspect. (2) seems like it is both more error prone and less flexible. **************************************************************** From: Randy Brukardt Sent: Wednesday, April 29, 2020 9:25 AM That's what I've been suggesting all along. **************************************************************** From: Richard Wai Sent: Wednesday, April 29, 2020 9:04 AM There is definitely a case these days for requiring atomic word/doubleword-sized registers. Most architectures that I’m familiar with, particularly RISC-V and ARM require the entire register to be accessed at once. My two cents is that (2) seems to be the most sane in practice. Since most architectures can’t support atomic operations larger than a (double)word anyways, the programmer will be restricted to subcomponents that are smaller than a (double)word. It wouldn’t make logical sense to have a record, ostensibly targeting a single register, to have non-atomic components anyways. From the top-down perspective, having a rule that says “if this record is atomic, all it’s components are always atomic” makes the most sense. Its an easy rule to understand. You’re not likely to have a large record capable of being atomic anyways, so I don’t see much use in allowing complex atomic and non-atomic mixes allowed by (3). Even huge architectures like the upcoming 128-bit RISC-V ISA, one would probably not benefit much from having non-atomic components in your 128 bit atomic record. **************************************************************** From: Tero Koskinen Sent: Thursday, April 30, 2020 2:20 PM Sorry, I am little late to this discussion, but I guess I can count as a real embedded practitioner. I usually work with 32-bit ARM Cortex-M{0,4,7,23,33} microcontrollers. They are quite common and relatively simple 32-bit processors with 4GB flat memory map and 16-bit & 32-bit instruction sets. The 4GB memory map contains various different areas which are mapped to different processor peripherals. Normal RAM is one area. GPIO pin control is another area. Builtin flash (non-volatile memory) is yet another area, and so on. Some areas, like RAM, do not not have alignment or size requirements/limits for access (8-bit, 16-bit, 32-bit read/write operation works - the address can even be unaligned in many cases). However, other areas, like flash, have much stricter alignment and size requirements. Some ARM Cortex-M processors have 16-bit write size requirement. Also the alignment must be 16-bit (even addresses). ARM Cortex-M processor from another vendor on the other hand might have 32-bit write size and alignment requirements for flash memory area. It is also case dependent what happens if you don't follow the requirements. In some cases the code fails silently. In other case, the processor goes into a failure mode (special interrupt handler for example, or reboot in worst case). In addition to above areas, ARM Cortex-M processors optionally can have so called bit-banded memory area. In this kind of area, one 32-bit data access for an address is mapped to a single bit in another address. So, for example, let's assume that 1kB bit-band area starts from 16#0000_F000# and is mapped to address 16#0010_0000#. Writing 16#0000_0001# to address 16#0000_F004# sets the second lowest bit to 1 at address 16#0010_0000#. (=> 32-bit reading from 16#0010_0000# will get us value 16#0000_0002# if the initial value was 0.) This allows atomic bit operations for certain memory areas. **************************************************************** From: Weston Pan Sent: Wednesday, April 29, 2020 6:05 AM There was a lot to read, so hopefully I understood it all. I personally think if an object/type is atomic, there should not be any atomic components/subcomponents within it. If a {sub}component is specified to be of an atomic type (or is a type that contains some), either it should be rejected or the atomicity ignored and a compiler warning is given. I'm agree with Randy that to allow atomic {sub}components to be buried within can lead to undesirable maintenance issues that would be hard to track. For register level programming, I have only needed atomicity for the register as whole (i.e. never for fields within a register). Outside of register level programming, I can't think of a reason why you would apply Atomic aspect at a higher level and at the same time at a piece-meal level too. Atomicity at the higher level is probably the only level that matters. For any finer grain of control, I would think it probably involves data that is large enough that it cannot be handled with simple CPU word read/write access, and instead constructs such as protected types would be needed instead. With that in mind, I think A-12-0128-1 should not have distinguished between nonatomic/atomic {sub}components and kept it simply as "{sub}components". **************************************************************** From: Bill Wong Sent: Wednesday, April 29, 2020 9:53 AM > I just checked briefly through the first few peripherals' register > descriptions in the STM32F4xxx Reference Manual issue 11. ... I was also going to mention this as I have been trying to get a runtime working for TI's chips and it has similar requirements. Hiding access behind a function is currently what I was working with but developers often what direct access. **************************************************************** From: Weston Pan Sent: Wednesday, April 29, 2020 2:23 PM I am surprised by Richard's comment. To me it does *not* make sense to have atomic subcomponents in a register. The register as a whole is consider atomic for a reason, specifically a hardware restriction. By allowing atomic operations on a subcomponent, then the issue that Randy pointed out about violating the proper memory access can occur (e.g. Randy's example of a byte read/write access being performed on a register that requires a 32-bit read/write). Regarding Simon's comment about ARM registers allowing different sized read access but a 32-bit write restriction, doesn't mean that different sized read operations need to be enforced/allowed on the register. I think the flexibility on reads is because having multiple read access is safe (e.g. similar to multiple readers of a protected object). However, that doesn't mean you have to allow all the types of reads on an Ada object that overlays the register. I think it makes more sense to go with the highest restriction, in Simon's case a 32-bit write access, and just apply that for reads as well (i.e. both read and write are similar). Granted, I can only go based on my limited experience at register programming, but unless someone can provide a good example of why having atomic subcomponents or even a mixture of non-atomic and atomic subcomponents for registers should be allowed, I still think atomic objects should be forbidden or removed when contained in an atomic object. **************************************************************** From: Weston Pan Sent: Wednesday, April 29, 2020 2:34 PM Minor correction to my last sentence: I still think atomic subcomponents at any deeper level should be forbidden or the atomic aspect for them should be ignored when contained within an atomic object. **************************************************************** From: Tucker Taft Sent: Thursday, May 14, 2020 1:05 PM In C.6(6.10/3) the RM says: If any of these aspects are directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), each of these aspects is False. It seems odd that Volatile and independent are False if not specified. We say that if you specify something is Atomic, then it is also Volatile. So doesn't that mean that specifying something as Atomic implicitly also specifies it to be Volatile? We certainly don't want to allow someone to specify Atomic => True, Volatile => False. It feels like the wording of this paragraph needs some work, or we need to establish a stronger rule that specifying Atomic => True implies both Volatile=>True and Independent=>True. **************************************************************** From: Jeff Cousins Sent: Thursday, May 14, 2020 1:34 PM Seems a good point. **************************************************************** From: Richard Wai Sent: Thursday, May 14, 2020 7:28 PM > It seems odd that Volatile and independent are False if not specified. > We say that if you specify something is Atomic, then it is also Volatile. So > doesn't that mean that specifying something as Atomic implicitly also > specifies it to be Volatile? That was certainly my expectation of Atomic.. I'm actually stunned that C.6 doesn't make this explicit anywhere. > We certainly don't want to allow someone to specify Atomic => True, > Volatile => False. I can imagine cases where you know that an atomic value is being changed synchronously, but yet you want any actual writes or reads to be atomic. For example, you are inside of an ISR that is reading some kind of buffer register. That would allow the user to map an object directly to the address of that register with Atomic => True, Volatile => False, and the compiler could "correctly" optimize-out multiple reads, thus avoiding the need to copy values. But this seems to be such a corner case, and such an insignificant advantage that it's hardly an argument to allow it. OTOH, this seems to create the potential for implementation-defined funny business. If we let users have Atomic => True, Volatile => False entities, then the user would need to make correct assumptions about how the compiler might optimize reads and writes. That seems pretty dangerous and un-Ada-like. It's even worse if we look at the write-side of the equation, where you probably don't want the compiler deciding if multiple writes of the same value to a register should be "optimized". Incidentally, I tested it and GNAT (GCC 9.2.0) accepts this: type My_ATM is mod 2**8 with Atomic => True, Volatile => False; It gave me no complaints. > It feels like the wording of this paragraph needs some work, or we > need to establish a stronger rule that specifying Atomic => > True implies both Volatile=>True and Independent=>True. Agreed. **************************************************************** From: Randy Brukardt Sent: Thursday, May 14, 2020 8:00 PM I think that the original pragmas were defined independently of each other, so the wording was written such that only one was specified. We probably left the wording that way. Even so, I agree that keeping Volatile and Independent matching Atomic might make more sense. However, in that case, I'd guess that some of the rules are redundant when they say that the same rule applies to Atomic as to Volatile. Especially the rule that says all atomic entities are volatile. Are you proposing wording changes and an AI write-up? :-) [How are you going to have time to do AdaCore work before the next meeting?? :-)] **************************************************************** From: Tucker Taft Sent: Thursday, May 14, 2020 8:07 PM Yes, I'll take a shot at it. Steve and I are debating the rule about formal array types and volatility as well, which I might fold into yet another C.6 fix-up. I think we still are debating the one having to do with atomicity and nesting. I wonder whether we ultimately need two aspects, Atomic which works the way the current one does, which allows nesting, and "Atomic_Full_Access" or equivalent which disallows any nested Atomic (or Atomic_Full_Access) subcomponents. Both of them would require rounding up of non-atomic subcomponents, but one would disallow having nested atomic subcomponents. **************************************************************** From: Randy Brukardt Sent: Thursday, May 14, 2020 9:38 PM I think Atomic_Full_Access would work; I've always viewed it as an option on the existing Atomic (I'd expect the rules would require far less change that way) but I don't think that it would be a problem to do it the other way (just more change is needed). **************************************************************** From: Tucker Taft Sent: Friday, May 15, 2020 9:09 AM We could have a separate "Full_Access_Only" or "No_Nested_Atomic" or something like that, which would only be meaningful in conjunction with Atomic. **************************************************************** From: Randy Brukardt Sent: Friday, May 15, 2020 12:42 PM Yup, that's what I was thinking. Didn't have a good name for it though. **************************************************************** From: Tucker Taft Sent: Saturday, June 13, 2020 1:01 PM After a bit of thought, I conclude this paragraph about formal array types: If a type with volatile components is used as an actual for a generic formal array type, then the components of the formal type shall be volatile. Furthermore, if the actual type has atomic components and the formal array type has aliased components, then the components of the formal array type shall also be atomic. Needs to have the following added at the end: A corresponding rule applies when the actual type has volatile full access components. ****************************************************************