Software-Defined Networking (SDN) switch vendors are interested in extending switch data planes to support new and continuously evolving network protocols (e.g., NVGRE, VXLAN). Numerous commercial programmable data plane devices already enable a programmer to specify various aspects of the data plane including packet parsing, actions, and the layout of packet processing on the hardware device itself. Unlike OpenFlow-based devices, which only expose a series of fixed match-action table (MAT) abstraction, these specialized devices provide a more flexible abstraction for packet processing. Despite the increased programmability that these devices offer, however, the architecture of the target restricts the features that can be exposed to the programmer. Similarly, existing languages for programming the data planes in such devices (e.g., P4) assume a specific computational model, resembling the architecture of the device for which they are targeted for. Unfortunately, this model leads to similar limitations as in OpenFlow, where the high-level specification is coupled to the underlying device model. In this paper, we introduce NetASM, an intermediate representation for programmable data planes. NetASM is a device-independent language that is expressive enough to act as the target language for compilers for high-level languages, yet low-level enough to be efficiently assembled on various device architectures. It enables conventional compiler optimization techniques to significantly improve the performance and resource utilization of custom packet-processing pipelines on a variety of targets.