I've tested some ideas with Volatile!T but there's always one remaining
problem:

In C people often define a macro to describe a MMIO location:
#define PORTB *((ubyte*)0x05)

which can then be used like this:
PORTB |= 0b1000_0000;

It's not really possible to represent this in D. There are workarounds,
but all have drawbacks. Naive approaches require space in the data
section. Defining PORTB as an enum ubyte* is working, but then operator
overloading doesn't work correctly (or the user always has to
dereference manually). immutable ubyte* also doesn't work because of
transitivity.

So I think we do need a way to specify this: I've got an extern
variable, and it's at this address. This is quite similar to
pragma(mangle), so in some way it seems natural to use this:

pragma(address, 0x05) extern ubyte PORTB;

But does this really make sense? What makes a variable a variable? For
example the GCC backends has builtin support for extern, static, const,
manifest variables, but no way to specify an address for an extern
variable. Is there a reason for this?

(Another solution are alias expressions but that's much more invasive.)


I've also implemented a small proof-of concept for this idea: Right now
I simply implemented the pragma and return a pointer dereference
expression from VarExp::sematic. This seems to work fine so far, but
are there other ways to access a variable without a VarExp or could
there be any other problems?



---------
import gcc.builtins;

struct Noop
{
    ubyte _data;

    void opOpAssign(string op)(in ubyte rhs) nothrow
    {
        ubyte val = __builtin_volatile_load(&_data);
        mixin("val" ~ op ~ "= rhs;");
        __builtin_volatile_store(&_data, val);
    }
}

pragma(address, 0x1000) extern Noop PORTB;

void main()
{
    auto addr = &PORTB;
    PORTB |= 0b1000_000;
}


;; Function D main (_Dmain)
;; enabled by -tree-original

{
  struct Noop * addr;

  (void) (addr = 4096B);
  opOpAssign (4096B, 64);
  return <retval> = 0;
}

;; Function opOpAssign
(_D4test4Noop25__T10opOpAssignVAyaa1_7cZ10opOpAssignMFNbxhZv) ;;
enabled by -tree-original

{
  ubyte val;

  if (this != 0)
    {
      <<< Unknown tree: void_cst >>>
    }
  else
    {
      _d_assert_msg ({.length=9, .ptr="null this"},
  {.length=9, .ptr="../test.d"}, 7); }
  (void) (val = (ubyte) *(volatile ubyte *) &this->_data);
  (void) (val = val | (ubyte) rhs);
  (void) (*(volatile ubyte *) &this->_data = val);
}


(With -O1 this generates perfect ASM. Of course once we have this
working there are much better ways to access the registers than
simple bit manipulation)

Reply via email to